Pull to refresh

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

Development for Android *
Sandbox
Совсем недавно пользователь 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
Tags:
Hubs:
Total votes 25: ↑23 and ↓2 +21
Views 9.3K
Comments Comments 26