android - Carga perezosa de imágenes en ListView
image url (30)
Estoy usando un ListView
para mostrar algunas imágenes y títulos asociados con esas imágenes. Estoy obteniendo las imágenes de internet. ¿Hay alguna forma de cargar las imágenes de forma perezosa, mientras el texto se muestra, la IU no se bloquea y las imágenes se muestran a medida que se descargan?
El número total de imágenes no es fijo.
Bueno, el tiempo de carga de imágenes desde Internet tiene muchas soluciones. También puede utilizar la biblioteca Android-Query . Te dará toda la actividad requerida. Asegúrate de lo que quieres hacer y lee la página wiki de la biblioteca. Y resolver la restricción de carga de la imagen.
Este es mi código:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView imageview = (ImageView) v.findViewById(R.id.icon);
AQuery aq = new AQuery(convertView);
String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";
aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
@Override
public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
iv.setImageBitmap(bm);
}
));
return v;
}
Se debe resolver su problema de carga perezosa.
Cargador de alto rendimiento: después de examinar los métodos sugeridos aquí, utilicé la solución de Ben con algunos cambios.
Me di cuenta de que trabajar con dibujables es más rápido que con mapas de bits, así que en lugar de eso utilizo dibujables
El uso de SoftReference es genial, pero hace que la imagen almacenada en caché se elimine con demasiada frecuencia, así que agregué una lista vinculada que contiene referencias de imágenes, impidiendo que la imagen se elimine, hasta que alcance un tamaño predefinido
Para abrir InputStream, utilicé java.net.URLConnection, que me permite usar la caché web (primero debe configurar una caché de respuesta, pero esa es otra historia)
Mi código:
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Collections;
import java.util.WeakHashMap;
import java.lang.ref.SoftReference;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
public class DrawableBackgroundDownloader {
private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public static int MAX_CACHE_SIZE = 80;
public int THREAD_POOL_SIZE = 3;
/**
* Constructor
*/
public DrawableBackgroundDownloader() {
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
/**
* Clears all instance data and stops running threads
*/
public void Reset() {
ExecutorService oldThreadPool = mThreadPool;
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
oldThreadPool.shutdownNow();
mChacheController.clear();
mCache.clear();
mImageViews.clear();
}
public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {
mImageViews.put(imageView, url);
Drawable drawable = getDrawableFromCache(url);
// check in UI thread, so no concurrency issues
if (drawable != null) {
//Log.d(null, "Item loaded from mCache: " + url);
imageView.setImageDrawable(drawable);
} else {
imageView.setImageDrawable(placeholder);
queueJob(url, imageView, placeholder);
}
}
private Drawable getDrawableFromCache(String url) {
if (mCache.containsKey(url)) {
return mCache.get(url).get();
}
return null;
}
private synchronized void putDrawableInCache(String url,Drawable drawable) {
int chacheControllerSize = mChacheController.size();
if (chacheControllerSize > MAX_CACHE_SIZE)
mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();
mChacheController.addLast(drawable);
mCache.put(url, new SoftReference<Drawable>(drawable));
}
private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {
/* Create handler in UI thread. */
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
String tag = mImageViews.get(imageView);
if (tag != null && tag.equals(url)) {
if (imageView.isShown())
if (msg.obj != null) {
imageView.setImageDrawable((Drawable) msg.obj);
} else {
imageView.setImageDrawable(placeholder);
//Log.d(null, "fail " + url);
}
}
}
};
mThreadPool.submit(new Runnable() {
@Override
public void run() {
final Drawable bmp = downloadDrawable(url);
// if the view is not visible anymore, the image will be ready for next time in cache
if (imageView.isShown())
{
Message message = Message.obtain();
message.obj = bmp;
//Log.d(null, "Item downloaded: " + url);
handler.sendMessage(message);
}
}
});
}
private Drawable downloadDrawable(String url) {
try {
InputStream is = getInputStream(url);
Drawable drawable = Drawable.createFromStream(is, url);
putDrawableInCache(url,drawable);
return drawable;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
URL url = new URL(urlString);
URLConnection connection;
connection = url.openConnection();
connection.setUseCaches(true);
connection.connect();
InputStream response = connection.getInputStream();
return response;
}
}
Creo que este problema es muy popular entre los desarrolladores de Android, y hay muchas bibliotecas de este tipo que pretenden resolver este problema, pero solo algunas de ellas parecen estar en la mira. AQuery es una de esas bibliotecas, pero es mejor que la mayoría de ellas en todos los aspectos y vale la pena intentarlo.
Debes probar este cargador universal es el mejor. Estoy usando esto después de hacer muchos RnD en la carga perezosa.
github.com/nostra13/Android-Universal-Image-Loader
Caracteristicas
- Carga de imágenes multiproceso (asíncrono o sincronizado)
- Amplia personalización de la configuración de ImageLoader (ejecutores de hilos, descargador, decodificador, memoria y caché de disco, opciones de imagen de pantalla, etc.)
- Muchas opciones de personalización para cada llamada de imagen de pantalla (imágenes de código auxiliar, interruptor de caché, opciones de decodificación, procesamiento y visualización de mapas de bits, etc.)
- Almacenamiento de imágenes en la memoria y / o en el disco (sistema de archivos del dispositivo o tarjeta SD)
- Proceso de carga de escucha (incluido el progreso de la descarga)
Soporte para Android 2.0+
Eche un vistazo a Shutterbug , el puerto ligero de SDWebImage (una biblioteca agradable en iOS) de Applidium para Android. Admite el almacenamiento en caché asíncrono, almacena direcciones URL fallidas, maneja bien la concurrencia y se incluyen subclases útiles.
¡Las solicitudes de extracción (y los informes de errores) también son bienvenidos!
Este es un problema común en Android que ha sido resuelto de muchas maneras por muchas personas. En mi opinión, la mejor solución que he visto es la biblioteca relativamente nueva llamada Picasso . Aquí están los aspectos más destacados:
- Código abierto, pero encabezado por
Jake Wharton
de ActionBarSherlock fame. - Cargue de forma asíncrona las imágenes de la red o los recursos de la aplicación con una línea de código
- Detección automática de
ListView
- Disco automático y caché de memoria.
- Se pueden hacer transformaciones personalizadas.
- Un montón de opciones configurables
- API super simple
- Actualizado frecuentemente
Esto es lo que creé para contener las imágenes que mi aplicación está mostrando actualmente. Tenga en cuenta que el objeto "Log" en uso aquí es mi envoltorio personalizado alrededor de la clase Log final dentro de Android.
package com.wilson.android.library;
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;
public class DrawableManager {
private final Map<String, Drawable> drawableMap;
public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}
public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}
return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}
He escrito un tutorial que explica cómo realizar una carga lenta de imágenes en una vista de lista. Entro en algunos detalles sobre los temas de reciclaje y concurrencia. También utilizo un grupo de subprocesos fijos para evitar generar muchos subprocesos.
He estado usando NetworkImageView de la nueva biblioteca de Android Volley com.android.volley.toolbox.NetworkImageView
, y parece estar funcionando bastante bien. Aparentemente, esta es la misma vista que se usa en Google Play y otras aplicaciones nuevas de Google. Definitivamente vale la pena echarle un vistazo.
He seguido este entrenamiento de Android y creo que hace un excelente trabajo descargando imágenes sin bloquear la interfaz de usuario principal. También maneja el almacenamiento en caché y se ocupa del desplazamiento a través de muchas imágenes: Cargar mapas de bits grandes de manera eficiente
Hice una simple demostración de una lista perezosa (ubicada en GitHub) con imágenes. Puede ser útil para alguien. Descarga imágenes en el hilo de fondo. Las imágenes se almacenan en caché en una tarjeta SD y en la memoria. La implementación del caché es muy simple y es suficiente para la demostración. Decodifico imágenes con inSampleSize para reducir el consumo de memoria. También trato de manejar las vistas recicladas correctamente.
La forma en que lo hago es iniciar un hilo para descargar las imágenes en segundo plano y entregarle una devolución de llamada para cada elemento de la lista. Cuando se termina de descargar una imagen, se llama a la devolución de llamada que actualiza la vista para el elemento de la lista.
Sin embargo, este método no funciona muy bien cuando estás reciclando vistas.
Recomiendo el instrumento de código abierto github.com/nostra13/Android-Universal-Image-Loader . Originalmente se basa en el proyecto LazyList de Fedor Vlasov y se ha mejorado enormemente desde entonces.
- Carga de imágenes multiproceso
- Posibilidad de ajuste amplio de la configuración de ImageLoader (ejecutores de subprocesos, decodificadores, decodificadores, memoria y caché de disco, opciones de imagen de pantalla, y otros)
- Posibilidad de almacenamiento en caché de imágenes en la memoria y / o en el sistema de archivos del dispositivo (o tarjeta SD)
- Posibilidad de "escuchar" el proceso de carga.
- Posibilidad de personalizar cada llamada de imagen de pantalla con opciones separadas
- Soporte de widgets
- Soporte para Android 2.0+
Solo quiero agregar un buen ejemplo más, Adaptadores XML . Como lo usa Google y también estoy usando la misma lógica para evitar un error de OutOfMemory.
Básicamente, este ImageDownloader es su respuesta (ya que cubre la mayoría de sus requisitos). Algunos también se pueden implementar en eso.
android-developers.blogspot.com/2010/07/… , un tutorial de Gilles Debunne.
Esto es del blog de desarrolladores de Android. El código sugerido utiliza:
-
AsyncTasks
. - Un
FIFO cache
duro, de tamaño limitado. - Un caché suave, fácilmente
garbage collect
. - Un marcador de posición
Drawable
mientras descargas.
DroidParts tiene ImageFetcher que no requiere configuración para comenzar.
- Utiliza un disco y memoria caché de uso menos reciente (LRU).
- Decodifica eficientemente las imágenes.
- Soporta la modificación de mapas de bits en subproceso de fondo.
- Tiene un fundido cruzado simple.
- Tiene una imagen cargando progreso de devolución de llamada.
Clone DroidPartsGram para un ejemplo:
URLImageViewHelper es una increíble biblioteca que te ayuda a hacerlo.
1. Picasso permite la carga de imágenes sin problemas en su aplicación, a menudo en una línea de código.
Utilice Gradle:
implementation ''com.squareup.picasso:picasso:2.71828''
Sólo una línea de código!
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. Glide Una biblioteca de carga y almacenamiento en caché de imágenes para Android enfocada en el desplazamiento suave
Utilice Gradle:
repositories {
mavenCentral()
google()
}
dependencies {
implementation ''com.github.bumptech.glide:glide:4.7.1''
annotationProcessor ''com.github.bumptech.glide:compiler:4.7.1''
}
// Para una vista simple:
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);
3. fresco es un poderoso sistema para mostrar imágenes en aplicaciones de Android.Fresco se encarga de cargar y mostrar las imágenes, para que no tenga que hacerlo.
Actualización: Tenga en cuenta que esta respuesta es bastante ineficaz ahora. El recolector de basura actúa agresivamente en SoftReference y WeakReference, por lo que este código NO es adecuado para nuevas aplicaciones. (En su lugar, intente bibliotecas como github.com/nostra13/Android-Universal-Image-Loader sugiere en otras respuestas).
Gracias a James por el código, y Bao-Long por la sugerencia de usar SoftReference. Implementé los cambios de SoftReference en el código de James. Desafortunadamente, SoftReferences hizo que mis imágenes se recolectaran con demasiada rapidez. En mi caso estuvo bien sin las cosas de SoftReference, porque el tamaño de mi lista es limitado y mis imágenes son pequeñas.
Hay una discusión de hace un año sobre las referencias de SoftReferences en los grupos de google: enlace al hilo . Como una solución para la recolección de basura demasiado temprana, sugieren la posibilidad de configurar manualmente el tamaño del montón de VM utilizando dalvik.system.VMRuntime.setMinimumHeapSize (), que no es muy atractivo para mí.
public DrawableManager() {
drawableMap = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable fetchDrawable(String urlString) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null)
return drawable;
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
drawableRef = new SoftReference<Drawable>(drawable);
drawableMap.put(urlString, drawableRef);
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
return drawableRef.get();
} catch (MalformedURLException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null) {
imageView.setImageDrawable(drawableRef.get());
return;
}
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
Picasso
Usa la biblioteca de Picasso de Jake Wharton. (Una biblioteca de carga de imágenes perfecta del desarrollador de ActionBarSherlock)
Una potente biblioteca de descarga y almacenamiento en caché de imágenes para Android.
Las imágenes agregan un contexto y un estilo visual muy necesarios a las aplicaciones de Android. Picasso permite la carga de imágenes sin problemas en su aplicación, a menudo en una línea de código.
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Picasso maneja automáticamente muchos de los inconvenientes comunes de la carga de imágenes en Android:
Manejo de reciclaje de ImageView y cancelación de descarga en un adaptador. Transformaciones complejas de imágenes con uso mínimo de memoria. Memoria automática y caché de disco.
Biblioteca de Picasso Jake Wharton
Deslizamiento
Glide es un marco de administración de medios de código abierto rápido y eficiente para Android que envuelve la decodificación de medios, la memoria y el almacenamiento en caché de disco, y la agrupación de recursos en una interfaz simple y fácil de usar.
Glide admite la captura, decodificación y visualización de imágenes fijas de video, imágenes y GIF animados. Glide incluye una api flexible que permite a los desarrolladores conectarse a casi cualquier pila de red. De manera predeterminada, Glide usa una pila personalizada basada en HttpUrlConnection, pero también incluye bibliotecas de utilidades conectadas al proyecto Volley de Google o a la biblioteca OkHttp de Square.
Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);
El objetivo principal de Glide es hacer que el desplazamiento de cualquier tipo de lista de imágenes sea lo más suave y rápido posible, pero Glide también es efectivo para casi cualquier caso en el que necesite buscar, cambiar el tamaño y mostrar una imagen remota.
Imagen de deslizamiento cargando biblioteca
Fresco por Facebook
Fresco es un poderoso sistema para mostrar imágenes en aplicaciones de Android.
Fresco se encarga de cargar y mostrar la imagen, por lo que no tiene que hacerlo. Cargará imágenes de la red, el almacenamiento local o los recursos locales, y mostrará un marcador de posición hasta que la imagen haya llegado. Tiene dos niveles de caché; Uno en memoria y otro en almacenamiento interno.
En Android 4.x e inferior, Fresco coloca las imágenes en una región especial de la memoria de Android. Esto permite que su aplicación se ejecute más rápido y sufra el temido OutOfMemoryError con mucha menos frecuencia.
Puedo recomendar una forma diferente que funciona como un encanto: Android Query.
Puede descargar ese archivo JAR desde here
AQuery androidAQuery = new AQuery(this);
Como ejemplo:
androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);
Es muy rápido y preciso, y al usarlo, puede encontrar muchas más funciones como la animación al cargar, obtener un mapa de bits (si es necesario), etc.
Solo un consejo rápido para alguien que está en indecisión sobre qué biblioteca usar para las imágenes de carga lenta:
Hay cuatro formas básicas.
DIY => No es la mejor solución pero para unas pocas imágenes y si quieres ir sin la molestia de usar otras bibliotecas
Volley''s Lazy Loading library => De chicos en Android. Es agradable y todo, pero está mal documentado y, por lo tanto, es un problema para usar.
Picasso: una solución simple que simplemente funciona, incluso puede especificar el tamaño de imagen exacto que quiere traer. Es muy simple de usar pero puede no ser muy "eficaz" para aplicaciones que tienen que lidiar con enormes cantidades de imágenes.
UIL: La mejor manera de cargar imágenes perezosas. Puede almacenar imágenes en caché (necesita permiso, por supuesto), inicializar el cargador una vez y luego hacer su trabajo. La biblioteca de carga asíncrona más madura que he visto hasta ahora.
Todos los códigos anteriores tienen su propio valor, pero con mi experiencia personal, solo pruébelo con Picasso.
Picasso es una biblioteca específicamente para este propósito, de hecho administrará el caché y todas las demás operaciones de red automáticamente. Deberá agregar una biblioteca en su proyecto y solo escribir una línea de código para cargar la imagen desde una URL remota.
Visite aquí: http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149
Novoda también tiene una gran biblioteca de carga de imágenes perezosas y muchas aplicaciones como Songkick, Podio, SecretDJ e ImageSearch utilizan su biblioteca.
Su biblioteca está alojada here en Github y también tienen un rastreador de problemas bastante activo . Su proyecto también parece ser bastante activo, con más de 300 confirmaciones al momento de escribir esta respuesta.
Prueba Android-Query . Tiene métodos increíblemente simples para cargar y almacenar en caché las imágenes de forma asíncrona.
Si quieres mostrar el diseño de Shimmer como Facebook, hay una biblioteca oficial de facebok para eso. FaceBook Shimmer Android
Se encarga de todo, solo tiene que colocar el código de diseño deseado de forma anidada en un marco brillante. Aquí hay un código de ejemplo.
<com.facebook.shimmer.ShimmerFrameLayout
android:id=“@+id/shimmer_view_container”
android:layout_width=“wrap_content”
android:layout_height="wrap_content"
shimmer:duration="1000">
<here will be your content to display />
</com.facebook.shimmer.ShimmerFrameLayout>
Y aquí está el código java para ello.
ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();
Agregue esta dependencia en su archivo de gradle.
implementation ''com.facebook.shimmer:shimmer:0.1.0@aar''
Tuve este problema e implementé lruCache. Creo que necesitas API 12 o superior o usar la biblioteca de compatibilidad v4. lurCache es una memoria rápida, pero también tiene un presupuesto, por lo que si le preocupa que pueda usar una caché de disco ... Todo se describe en developer.android.com/training/displaying-bitmaps/… .
Ahora proporcionaré mi implementación, que es un singleton que llamo desde cualquier lugar así:
//Where the first is a string and the other is a imageview to load.
DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);
Aquí está el código ideal para almacenar en caché y luego llamar a lo anterior en getView de un adaptador al recuperar la imagen web:
public class DownloadImageTask {
private LruCache<String, Bitmap> mMemoryCache;
/* Create a singleton class to call this from multiple classes */
private static DownloadImageTask instance = null;
public static DownloadImageTask getInstance() {
if (instance == null) {
instance = new DownloadImageTask();
}
return instance;
}
//Lock the constructor from public instances
private DownloadImageTask() {
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
public void loadBitmap(String avatarURL, ImageView imageView) {
final String imageKey = String.valueOf(avatarURL);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
}
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
/* A background process that opens a http stream and decodes a web image. */
class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTaskViaWeb(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon = BitmapFactory.decodeStream(in);
}
catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);
return mIcon;
}
/* After decoding we update the view on the main UI. */
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}
Usa la biblioteca de planeo. Funcionó para mí y también lo será para tu código. Funciona tanto para imágenes como para gifs.
ImageView imageView = (ImageView) findViewById(R.id.test_image);
GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
Glide
.with(this)
.load(url)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(imagePreview);
}
public class ImageDownloader {
Map<String, Bitmap> imageCache;
public ImageDownloader() {
imageCache = new HashMap<String, Bitmap>();
}
// download function
public void download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
// Caching code right here
String filename = String.valueOf(url.hashCode());
File f = new File(getCacheDirectory(imageView.getContext()),
filename);
// Is the bitmap in our memory cache?
Bitmap bitmap = null;
bitmap = (Bitmap) imageCache.get(f.getPath());
if (bitmap == null) {
bitmap = BitmapFactory.decodeFile(f.getPath());
if (bitmap != null) {
imageCache.put(f.getPath(), bitmap);
}
}
// No? download it
if (bitmap == null) {
try {
BitmapDownloaderTask task = new BitmapDownloaderTask(
imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
} catch (Exception e) {
Log.e("Error==>", e.toString());
}
} else {
// Yes? set the image
imageView.setImageBitmap(bitmap);
}
}
}
// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
bitmapDownloaderTask.cancel(true);
} else {
// The same URL is already being downloaded.
return false;
}
}
return true;
}
// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
return downloadedDrawable.getBitmapDownloaderTask();
}
}
return null;
}
// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
String sdState = android.os.Environment.getExternalStorageState();
File cacheDir;
if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
File sdDir = android.os.Environment.getExternalStorageDirectory();
// TODO : Change your diretcory here
cacheDir = new File(sdDir, "data/ToDo/images");
} else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
return cacheDir;
}
private void writeFile(Bitmap bmp, File f) {
FileOutputStream out = null;
try {
out = new FileOutputStream(f);
bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null)
out.close();
} catch (Exception ex) {
}
}
}
// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
url = (String) params[0];
return downloadBitmap(params[0]);
}
@Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with
// it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
// cache the image
String filename = String.valueOf(url.hashCode());
File f = new File(
getCacheDirectory(imageView.getContext()), filename);
imageCache.put(f.getPath(), bitmap);
writeFile(bitmap, f);
}
}
}
}
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.WHITE);
bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
// the actual download code
static Bitmap downloadBitmap(String url) {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1);
HttpClient client = new DefaultHttpClient(params);
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode
+ " while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from "
+ url + e.toString());
} finally {
if (client != null) {
// client.close();
}
}
return null;
}
}