Almacenamiento en caché de imágenes de Android

¿Cómo puedo almacenar en caché las imágenes después de que se descargan de la web?

Y ahora la frase clave: use la memoria caché del sistema.

URL url = new URL(strUrl); URLConnection connection = url.openConnection(); connection.setUseCaches(true); Object response = connection.getContent(); if (response instanceof Bitmap) { Bitmap bitmap = (Bitmap)response; } 

Proporciona memoria y caché flash-rom, compartida con el navegador.

Grr. Ojalá alguien me hubiera dicho que antes de escribir mi propio administrador de caché.

En cuanto a la elegante solución connection.setUseCaches anterior: lamentablemente, no funcionará sin un esfuerzo adicional. Necesitará instalar un ResponseCache usando ResponseCache.setDefault . De lo contrario, HttpURLConnection ignorará silenciosamente el setUseCaches(true) .

Vea los comentarios en la parte superior de FileResponseCache.java para más detalles:

http://libs-for-android.googlecode.com/svn/reference/com/google/android/filecache/FileResponseCache.html

(Publicaría esto en un comentario, pero aparentemente no tengo suficiente karma SO.)

Conviértelos en mapas de bits y guárdelos en una colección (HashMap, lista, etc.) o puede escribirlos en la tarjeta SD.

Al almacenarlos en el espacio de la aplicación utilizando el primer enfoque, es posible que desee envolverlos alrededor de java.lang.ref.SoftReference específicamente si sus números son grandes (para que sean basura recolectada durante una crisis). Esto podría seguir una recarga, sin embargo.

 HashMap> imageCache = new HashMap>(); 

escribirlos en SDcard no requerirá una recarga; solo un permiso de usuario.

Use LruCache para almacenar en caché las imágenes de manera eficiente. Puedes leer sobre LruCache del sitio para desarrolladores de Android

He utilizado la siguiente solución para la descarga de imágenes y el almacenamiento en caché en Android. Puedes seguir los pasos a continuación:

PASO 1: haga una clase llamada ImagesCache . He usado el Singleton object for this class

 import android.graphics.Bitmap; import android.support.v4.util.LruCache; public class ImagesCache { private LruCache imagesWarehouse; private static ImagesCache cache; public static ImagesCache getInstance() { if(cache == null) { cache = new ImagesCache(); } return cache; } public void initializeCache() { final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024); final int cacheSize = maxMemory / 8; System.out.println("cache size = "+cacheSize); imagesWarehouse = new LruCache(cacheSize) { protected int sizeOf(String key, Bitmap value) { // The cache size will be measured in kilobytes rather than number of items. int bitmapByteCount = value.getRowBytes() * value.getHeight(); return bitmapByteCount / 1024; } }; } public void addImageToWarehouse(String key, Bitmap value) { if(imagesWarehouse != null && imagesWarehouse.get(key) == null) { imagesWarehouse.put(key, value); } } public Bitmap getImageFromWarehouse(String key) { if(key != null) { return imagesWarehouse.get(key); } else { return null; } } public void removeImageFromWarehouse(String key) { imagesWarehouse.remove(key); } public void clearCache() { if(imagesWarehouse != null) { imagesWarehouse.evictAll(); } } } 

PASO 2:

hacer otra clase llamada DownloadImageTask que se utiliza si el bitmap no está disponible en la memoria caché lo descargará desde aquí:

 public class DownloadImageTask extends AsyncTask { private int inSampleSize = 0; private String imageUrl; private BaseAdapter adapter; private ImagesCache cache; private int desiredWidth, desiredHeight; private Bitmap image = null; private ImageView ivImageView; public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) { this.adapter = adapter; this.cache = ImagesCache.getInstance(); this.desiredWidth = desiredWidth; this.desiredHeight = desiredHeight; } public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight) { this.cache = cache; this.ivImageView = ivImageView; this.desiredHeight = desireHeight; this.desiredWidth = desireWidth; } @Override protected Bitmap doInBackground(String... params) { imageUrl = params[0]; return getImage(imageUrl); } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if(result != null) { cache.addImageToWarehouse(imageUrl, result); if(ivImageView != null) { ivImageView.setImageBitmap(result); } else { } if(adapter != null) { adapter.notifyDataSetChanged(); } } } private Bitmap getImage(String imageUrl) { if(cache.getImageFromWarehouse(imageUrl) == null) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inSampleSize = inSampleSize; try { URL url = new URL(imageUrl); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); InputStream stream = connection.getInputStream(); image = BitmapFactory.decodeStream(stream, null, options); int imageWidth = options.outWidth; int imageHeight = options.outHeight; if(imageWidth > desiredWidth || imageHeight > desiredHeight) { System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight); inSampleSize = inSampleSize + 2; getImage(imageUrl); } else { options.inJustDecodeBounds = false; connection = (HttpURLConnection)url.openConnection(); stream = connection.getInputStream(); image = BitmapFactory.decodeStream(stream, null, options); return image; } } catch(Exception e) { Log.e("getImage", e.toString()); } } return image; } 

PASO 3: Uso de su Activity o Adapter

Nota: si desea cargar imágenes desde la URL de Activity Class. Use el segundo Constructor de DownloadImageTask , pero si desea visualizar la imagen desde el Adapter use primero el Constructor de DownloadImageTask (por ejemplo, tiene una imagen en ListView y está configurando una imagen desde ‘Adaptador’)

USO DE LA ACTIVIDAD:

 ImageView imv = (ImageView) findViewById(R.id.imageView); ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class. cache.initializeCache(); String img = "your_image_url_here"; Bitmap bm = cache.getImageFromWarehouse(img); if(bm != null) { imv.setImageBitmap(bm); } else { imv.setImageBitmap(null); DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor. imgTask.execute(img); } 

USO DEL ADAPTADOR:

 ImageView imv = (ImageView) rowView.findViewById(R.id.imageView); ImagesCache cache = ImagesCache.getInstance(); cache.initializeCache(); String img = "your_image_url_here"; Bitmap bm = cache.getImageFromWarehouse(img); if(bm != null) { imv.setImageBitmap(bm); } else { imv.setImageBitmap(null); DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor. imgTask.execute(img); } 

Nota:

cache.initializeCache() puede usar esta statement en la primera Actividad de su aplicación. Una vez que hayas inicializado la caché, nunca necesitarás inicializarla cada vez que ImagesCache instancia de ImagesCache .

Nunca soy bueno para explicar cosas, pero espero que esto ayude a los principiantes a cómo almacenar en caché usando LruCache y su uso 🙂

EDITAR:

Hoy en día existen bibliotecas muy famosas conocidas como Picasso y Glide que se pueden utilizar para cargar imágenes de manera muy eficiente en la aplicación de Android. Prueba esta biblioteca muy simple y útil Picasso para Android y Glide para Android . No necesita preocuparse por las imágenes de caché.

Picasso permite cargar imágenes sin inconvenientes en su aplicación, ¡a menudo en una línea de código!

Glide, al igual que Picasso, puede cargar y mostrar imágenes de muchas fonts, a la vez que se ocupa del almacenamiento en caché y mantiene un bajo impacto de memoria al realizar manipulaciones de imágenes. Ha sido utilizado por las aplicaciones oficiales de Google (como la aplicación para Google I / O 2015) y es tan popular como Picasso. En esta serie, vamos a explorar las diferencias y las ventajas de Glide sobre Picasso.

También puedes visitar el blog para ver la diferencia entre Glide y Picasso

Para descargar una imagen y guardarla en la tarjeta de memoria, puede hacerlo así.

 //First create a new URL object URL url = new URL("http://sofes.miximages.com/android/holiday09_2.gif") //Next create a file, the example below will save to the SDCARD using JPEG format File file = new File("/sdcard/example.jpg"); //Next create a Bitmap object and download the image to bitmap Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); //Finally compress the bitmap, saving to the file previously created bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file)); 

No olvides agregar el permiso de Internet a tu manifiesto:

  

Consideraría usar el caché de imágenes de droidfu. Implementa una caché de imágenes en memoria y en disco. También obtienes un WebImageView que aprovecha la biblioteca de ImageCache.

Aquí está la descripción completa de droidfu y WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/

Probé SoftReferences, son reclamados de manera tan agresiva en Android que sentí que no tenía sentido usarlos

Como Thunder Rabbit sugirió, ImageDownloader es el mejor para el trabajo. También encontré una pequeña variación de la clase en:

http://theandroidcoder.com/utilities/android-image-download-and-caching/

La diferencia principal entre los dos es que ImageDownloader utiliza el sistema de almacenamiento en caché de Android, y el modificado utiliza el almacenamiento interno y externo como almacenamiento en caché, manteniendo las imágenes en caché de forma indefinida o hasta que el usuario lo elimine manualmente. El autor también menciona compatibilidad con Android 2.1.

Esta es una buena captura de Joe. El ejemplo de código anterior tiene dos problemas, uno: el objeto de respuesta no es una instancia de bitmap (cuando mi URL hace referencia a un jpg, como http: \ website.com \ image.jpg, es un

org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).

En segundo lugar, como señala Joe, no se produce el almacenamiento en caché sin la configuración de un caché de respuesta. Los desarrolladores de Android tienen que rodar su propio caché. Aquí hay un ejemplo para hacerlo, pero solo se almacena en la memoria caché, lo que realmente no es la solución completa.

http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/

La API de caché URLConnection se describe aquí:

http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html

Sigo pensando que esta es una solución correcta para seguir esta ruta, pero aún tienes que escribir un caché. Suena divertido, pero prefiero escribir características.

Hay una entrada especial en la sección de entrenamiento oficial de Android sobre esto: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

La sección es bastante nueva, no estaba allí cuando se hizo la pregunta.

La solución sugerida es usar un LruCache. Esa clase se introdujo en Honeycomb, pero también se incluye en la biblioteca de compatibilidad.

Puede inicializar un LruCache estableciendo el número máximo de entradas y las clasificará automáticamente y las borrará menos usadas cuando sobrepase el límite. Aparte de eso, se usa como un mapa normal.

El código de muestra de la página oficial:

 private LruCache mMemoryCache; @Override protected void onCreate(Bundle savedInstanceState) { ... // Get memory class of this device, exceeding this amount will throw an // OutOfMemory exception. final int memClass = ((ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE)).getMemoryClass(); // Use 1/8th of the available memory for this memory cache. final int cacheSize = 1024 * 1024 * memClass / 8; mMemoryCache = new LruCache(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in bytes rather than number of items. return bitmap.getByteCount(); } }; ... } public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } 

Anteriormente, SoftReferences era una buena alternativa, pero ya no, citando de la página oficial:

Nota: En el pasado, una implementación de memoria caché popular era una memoria caché de mapas de bits de referencia blanda o de referencia débil, sin embargo, esto no es recomendable. A partir de Android 2.3 (Nivel 9 de la API), el recolector de basura es más agresivo al recostackr referencias débiles / débiles, lo que las hace bastante ineficaces. Además, antes de Android 3.0 (API nivel 11), los datos de respaldo de un bitmap se almacenaron en la memoria nativa, que no se publica de manera predecible, lo que puede causar que una aplicación exceda brevemente sus límites de memoria y falle.

Considere usar la biblioteca de Universal Image Loader por Sergey Tarasevich . Viene con:

  • Carga de imágenes multiproceso. Le permite definir el tamaño del grupo de subprocesos
  • Almacenamiento en caché de imágenes en la memoria, en el sistema de archivos del dispositivo y en la tarjeta SD.
  • Posibilidad de escuchar el progreso de carga y carga de eventos

Universal Image Loader permite una gestión detallada del caché para las imágenes descargadas, con las siguientes configuraciones de caché:

  • UsingFreqLimitedMemoryCache : el bitmap menos utilizado se elimina cuando se excede el límite de tamaño de caché.
  • LRULimitedMemoryCache : el bitmap utilizado menos recientemente se elimina cuando se excede el límite de tamaño de caché.
  • FIFOLimitedMemoryCache : la regla FIFO se utiliza para eliminación cuando se excede el límite de tamaño de caché.
  • LargestLimitedMemoryCache : el bitmap más grande se elimina cuando se excede el límite de tamaño de caché.
  • LimitedAgeMemoryCache : el objeto en caché se elimina cuando su antigüedad excede el valor definido .
  • WeakMemoryCache : memoria caché con solo referencias débiles a bitmaps.

Un simple ejemplo de uso:

 ImageView imageView = groupView.findViewById(R.id.imageView); String imageUrl = "http://site.com/image.png"; ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.init(ImageLoaderConfiguration.createDefault(context)); imageLoader.displayImage(imageUrl, imageView); 

Este ejemplo utiliza el valor predeterminado UsingFreqLimitedMemoryCache .

Lo que realmente funcionó para mí fue configurar ResponseCache en mi clase principal:

 try { File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http"); long httpCacheSize = 10 * 1024 * 1024; // 10 MiB HttpResponseCache.install(httpCacheDir, httpCacheSize); } catch (IOException e) { } 

y

 connection.setUseCaches(true); 

al descargar bitmap.

http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html

Libs-for-android de Google tiene unas buenas bibliotecas para administrar la imagen y el caché de archivos.

http://code.google.com/p/libs-for-android/

Había estado luchando con esto por un tiempo; las respuestas usando SoftReferences perderían sus datos demasiado rápido. Las respuestas que sugieren la creación de una RequestCache eran demasiado desordenadas, además de que nunca pude encontrar un ejemplo completo.

Pero ImageDownloader.java funciona maravillosamente para mí. Utiliza un HashMap hasta que se alcanza la capacidad o hasta que se agota el tiempo de espera de purga, luego las cosas se mueven a una referencia suave, utilizando de ese modo lo mejor de ambos mundos.

Incluso una respuesta más tardía, pero escribí un Administrador de imágenes de Android que maneja el almacenamiento en caché de forma transparente (memoria y disco). El código está en Github https://github.com/felipecsl/Android-ImageManager

Última respuesta, pero pensé que debería agregar un enlace a mi sitio porque he escrito un tutorial sobre cómo crear una memoria caché de imágenes para Android: http://squarewolf.nl/2010/11/android-image-cache/ Actualización: el la página se ha desconectado porque la fuente no está actualizada. Me uno a @elenasys en su consejo para usar Ignition .

Entonces, a todas las personas que tropiezan con esta pregunta y no han encontrado una solución: ¡espero que disfruten! = D