Совсем недавно пользователь sly2m описал свой метод сохранения изображений из ImageView на SD карту телефона. Кто-то (лично я например) ожидал от этого поста нечто иное, а именно:
1. Работа с изображениями из Интернета
2. Автоматическая загрузка и сохранение таких изображений
3. Продвинутое кеширование изображений
Если заинтересовало — прошу взглянуть.
Итак, мы напишем свой класс, который будет выполнять все три задачи, которые нам необходимы.
Для начала создадим каркас класса и парочку дополнительных методов, которые пригодятся нам далее:
Назначение функции md5 думаю понятно, а вот функция fileSave будет сохранять любой InputStream в FileOutputStream, то-есть загружать наши картинки из сети и сохранять их на SD карту.
Далее, создаем вектор downloaded для хранения уже загруженных картинок, и метод для работы с таким вектором.
Это необходимо для устранения такого неприятного эффекта, как выполнение сразу нескольких потоков загрузки например для элементов ImageView внутри ListView.
Метод findObject будет искать поставленные в очередь картинки и возвращать true, если оная найдена.
А теперь два главных метода в нашем классе:
Итак, метод downloadImage(Activity activity, int cacheTime, String iUrl, ImageView iView) выполняет саму загрузку изображений из Сети и ее кеширование.
Ее параметры:
Но работать мы будем не с ним (не заметили модификатор private?), а с функцией fetchImage, которая принимает те-же самые параметры, что и у downloadImage. Сам метод fetchImage следит за списком загрузки и установку изображений в ImageView.
По моему скромному мнению, код интуитивно понятен и в комментариях не нуждается. В конце своего поста приведу пример использования класса:
UPDATE: Исходный код класса
UPDATE 2: Переписан метод fetchImage с использованием AsyncTask
UPDATE 3: Activity изменен на Context
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