Как стать автором
Обновить

Кеширование изображений на SD карте

Время на прочтение3 мин
Количество просмотров9.6K
Совсем недавно пользователь sly2m описал свой метод сохранения изображений из ImageView на SD карту телефона. Кто-то (лично я например) ожидал от этого поста нечто иное, а именно:

1. Работа с изображениями из Интернета
2. Автоматическая загрузка и сохранение таких изображений
3. Продвинутое кеширование изображений

Если заинтересовало — прошу взглянуть.

Итак, мы напишем свой класс, который будет выполнять все три задачи, которые нам необходимы.

Для начала создадим каркас класса и парочку дополнительных методов, которые пригодятся нам далее:
package com.habra.imagemanager;

public class ImageManager {
	public String md5(String s) {
		try {
			MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
			digest.update(s.getBytes());
			byte messageDigest[] = digest.digest();
			StringBuffer hexString = new StringBuffer();
			for (int i = 0; i < messageDigest.length; i++) {
				hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
			}
			return hexString.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return "";
	}
	public static void fileSave(InputStream is, FileOutputStream outputStream) {
		int i;
		try {
			while ((i = is.read()) != -1) {
				outputStream.write(i);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}


Назначение функции md5 думаю понятно, а вот функция fileSave будет сохранять любой InputStream в FileOutputStream, то-есть загружать наши картинки из сети и сохранять их на SD карту.

Далее, создаем вектор downloaded для хранения уже загруженных картинок, и метод для работы с таким вектором.
Это необходимо для устранения такого неприятного эффекта, как выполнение сразу нескольких потоков загрузки например для элементов ImageView внутри ListView.

private Vector<ImageView> downloaded = new Vector<ImageView>();
public boolean findObject(ImageView object) {
	for (int i = 0; i < downloaded.size(); i++) {
		if (downloaded.elementAt(i).equals(object)) {
			return true;
		}
	}
	return false;
}


Метод findObject будет искать поставленные в очередь картинки и возвращать true, если оная найдена.

А теперь два главных метода в нашем классе:

private Bitmap downloadImage(Context context, int cacheTime, String iUrl, ImageView iView) {
	Bitmap bitmap = null;
	if (cacheTime != 0) {
		File file = new File(context.getExternalCacheDir(), md5(iUrl)
				+ ".cache");
		long time = new Date().getTime() / 1000;
		long timeLastModifed = file.lastModified() / 1000;
		try {
			if (file.exists()) {
				if (timeLastModifed + cacheTime < time) {
					file.delete();
					file.createNewFile();
					fileSave(new URL(iUrl).openStream(),
							new FileOutputStream(file));
				}
			} else {
				file.createNewFile();
				fileSave(new URL(iUrl).openStream(), new FileOutputStream(
						file));
			}
			bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (bitmap == null) {
			file.delete();
		}
	} else {
		try {
			bitmap = BitmapFactory.decodeStream(new URL(iUrl).openStream());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	if (iView != null) {
		downloaded.remove(iView);
	}
	return bitmap;
}
public void fetchImage(final Context context, final int cacheTime, final String url, final ImageView iView) {
	if (iView != null) {
		if (findObject(iView)) {
			return;
		}
		downloaded.add(iView);
	}
	new AsyncTask<String, Void, Bitmap>() {
		protected Bitmap doInBackground(String... iUrl) {
			return downloadImage(context, cacheTime, iUrl[0], iView);
		}
		protected void onPostExecute(Bitmap result) {
			super.onPostExecute(result);
			if (iView != null) {
				iView.setImageBitmap(result);
			}
		}
	}.execute(new String[] { url });
}


Итак, метод downloadImage(Activity activity, int cacheTime, String iUrl, ImageView iView) выполняет саму загрузку изображений из Сети и ее кеширование.
Ее параметры:
  • Activity activity — активити приложения, нужно для определения пути для хранения кешированных файлов
  • int cacheTime — время релевантности кеша, в секундах
  • String iUrl — URL изображения
  • ImageView iView — ImageView, которому необходимо установить изображение


Но работать мы будем не с ним (не заметили модификатор private?), а с функцией fetchImage, которая принимает те-же самые параметры, что и у downloadImage. Сам метод fetchImage следит за списком загрузки и установку изображений в ImageView.
По моему скромному мнению, код интуитивно понятен и в комментариях не нуждается. В конце своего поста приведу пример использования класса:

ImageManager man = new ImageManager();

ImageView i1 = (ImageView) findViewById(R.id.i1);
ImageView i2 = (ImageView) findViewById(R.id.i2);
ImageView i3 = (ImageView) findViewById(R.id.i3);

man.fetchImage(this, 3600, "http://habrastorage.org/storage1/51624865/5d7f2b56/333c3c3f/fa5cdc9b.png", i1);
man.fetchImage(this, 3600, "http://habrastorage.org/storage1/9042dd3c/acc1f8b3/782ca380/c05ecaf3.png", i2);
man.fetchImage(this, 3600, "http://habrastorage.org/storage1/39a0bbce/4f56b8c7/ca84d78f/8b7bf972.png", i3);


UPDATE: Исходный код класса
UPDATE 2: Переписан метод fetchImage с использованием AsyncTask
UPDATE 3: Activity изменен на Context
Теги:
Хабы:
Всего голосов 25: ↑23 и ↓2+21
Комментарии26

Публикации

Истории

Работа

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
11 сентября
Митап по BigData от Честного ЗНАКа
Санкт-ПетербургОнлайн
14 сентября
Конференция Practical ML Conf
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн