Совсем недавно пользователь 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