Cómo cargar imágenes de forma diferida en ListView en Android

1931

Estoy usando a ListViewpara mostrar algunas imágenes y subtítulos asociados con esas imágenes. Estoy recibiendo las imágenes de Internet. ¿Hay alguna manera de cargar imágenes de forma diferida para que mientras se muestre el texto, la IU no esté bloqueada y las imágenes se muestren a medida que se descargan?

El número total de imágenes no es fijo.

perdido en el transito
fuente
10
Puede usar AsyncImageView de GreenDroid . Solo llama setUrl.
Pascal Dimassimo
77
Lo he usado Es una implementación maravillosa. Malas noticias de que AsyncImageView es parte de un gran proyecto GreenDroid, que hace que su aplicación sea más grande, incluso en el caso de que todo lo que necesite sea AsyncImageView. Además, parece que el proyecto GreenDroid no se actualiza desde 2011.
borisstr
55
Incluso puede probar esta biblioteca: Android-http-image-manager, que en mi opinión es la mejor para la carga asíncrona de imágenes.
Ritesh Kumar Dubey el
34
Solo use Picasso, hará todo por sí mismo. 'Picasso.with (yourContext) .load (img src / path / drawable here) .into (imageView, es decir, su objetivo);' ¡Eso es!
Anuj Sharma el
66
intente usar: github.com/nostra13/Android-Universal-Image-Loader , esta biblioteca es muy rápida y eficiente para la carga diferida y el almacenamiento en caché de imágenes
krunal patel

Respuestas:

1087

Esto es lo que creé para contener las imágenes que mi aplicación muestra actualmente. Tenga en cuenta que el objeto "Log" en uso aquí es mi contenedor 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();
    }
}
James A Wilson
fuente
104
Creo que debería usar SoftReferences para que su programa nunca cause una excepción OutOfMemoryException. Como GC puede borrar las softreferences cuando el tamaño de almacenamiento dinámico está aumentando ... puede administrar su propia generación, después de unos segundos puede poner sus imágenes en esa lista y antes de cargar, debe verificar que si la imagen existe, no la descargue nuevamente, sino que recopile de esa lista y también ponerlo de nuevo en su lista de softref y después de algún tiempo puede purgar su lista dura :)
AZ_
36
El proyecto Google Shelves es un excelente ejemplo de cómo lo hicieron code.google.com/p/shelves
AZ_
12
¿No te pierdes un retorno cuando drawableMap contiene la imagen ... sin iniciar el hilo de recuperación?
Karussell
55
Este código tiene varios problemas. En primer lugar, debe almacenar en caché los Drawables, lo que provocará una pérdida de memoria: stackoverflow.com/questions/7648740/… . En segundo lugar, el caché en sí nunca se borra, por lo que crecerá para siempre, esa es otra pérdida de memoria.
satur9nine
10
nadie ha oído hablar de LRU Cache developer.android.com/training/displaying-bitmaps/…
Muhammad Babar
1025

Hice una demostración simple de una lista perezosa (ubicada en GitHub) con imágenes.

Uso básico

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

No olvide agregar los siguientes permisos a su AndroidManifest.xml:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

cree solo una instancia de ImageLoader y reutilícela en toda su aplicación. De esta forma, el almacenamiento en caché de imágenes será mucho más eficiente.

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 de la memoria 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.

texto alternativo

Fedor
fuente
77
Gracias, estoy usando una versión ligeramente modificada de su código en casi todas partes de mi proyecto: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/…
shaman.sir
3
¿Alguien ha mirado el código por Gilles Debunne como alternativa? Las dos grandes diferencias que veo son 1) en el almacenamiento en memoria caché frente a la tarjeta SD y 2) el uso de AsyncTasks en lugar de un grupo de subprocesos. android-developers.blogspot.com/2010/07/…
Richard
44
Hay un error, a veces se activa: 10-13 09: 58: 46.738: ERROR / AndroidRuntime (24250): Controlador no capturado: salida principal del hilo debido a una excepción no detectada 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: Índice de matriz fuera de rango: 0 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): en java.util.Vector.elementAt (Vector.java:331) 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): en java.util.Vector.get (Vector.java:445) 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): en com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java:91)
Mikooos el
64
Me gustaría agregar para los novatos como yo que si desea agregar este código a su propio proyecto, debe agregar permisos de caché externo en el manifiesto. Gracias
Adam
2
@BruceHill No hay photosQueue en el último código fuente. Parece que estás usando una versión muy antigua.
Fedor
552

Recomiendo el instrumento de código abierto 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 ajustar ampliamente la configuración de ImageLoader (ejecutores de subprocesos, downlaoder, decodificador, memoria y caché de disco, opciones de visualización de imágenes 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+

nostra13
fuente
16
si está buscando un excelente código de "carga de imágenes" que su creador trata y mantiene como su precioso bebé (no solo una solución única), lo acaba de encontrar; big ups Nostra
AndroidGecko
Realmente un gran trabajo, pero va un poco por debajo de mi vista de lista. Si pudieras distinguir el mejor rendimiento de la compilación 'ImageLoader' ... o tal vez sea porque 'DisplayImageOptions'.
dinigo
Me encanta mucho esta vista de cuadrícula, pero no puedo usarla para mi tutorial. Soy un principiante de Android. Realicé una solicitud sobre gridview pero mi aplicación era targetSdkVersion 15, por lo que necesito usar Asynctask para el proceso en segundo plano. Solía ​​usar esta vista de cuadrícula pero no funciona. ¿Cómo puedo usarlo en targetSdkVersion 15?
kongkea
Puede crear una pregunta separada con la etiqueta [universal-image-loader]
nostra13
3
Después de leer los documentos oficiales de Android y de tratar de hacer esto yo mismo, estoy bastante seguro de que esta biblioteca es el final de la carga de imágenes. No puedo votar lo suficiente.
Núcleo
159

Multithreading para rendimiento , un tutorial de Gilles Debunne.

Esto es del Blog de desarrolladores de Android. El código sugerido usa:

  • AsyncTasks.
  • Un tamaño duro y limitado, FIFO cache .
  • Un suave, fácilmente garbage collect caché usar.
  • Un marcador de posición Drawable mientras descargas.

ingrese la descripción de la imagen aquí

Thomas Ahle
fuente
10
Funciona bien en 2.1 también. Simplemente no use AndroidHttpClient.
Thomas Ahle
2
@ thomas-ahle Gracias, vi que AndroidHttpClient estaba dando errores en 2.1, ya que está implementado desde 2.2, pero realmente no intenté encontrar algo más para reemplazarlo.
Adinia
44
@Adina Tienes razón, lo olvidé. Sin embargo, no hay nada en la receta que no pueda hacerse con un HttpClient normal.
Thomas Ahle
He oído en varios lugares que Google no recomienda referencias suaves porque el kernel de Android está muy ansioso por recopilar estas referencias en comparación con las versiones anteriores del sistema.
Muhammad Ahmed AbuTalib
109

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, pruebe bibliotecas como Universal Image Loader sugerido en otras respuestas).

Gracias a James por el código y a 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 recogieran demasiado rápido. En mi caso, estaba 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 con respecto a las SoftReferences en los grupos de Google: enlace al hilo . Como solución a la recolección de basura demasiado temprana, sugieren la posibilidad de configurar manualmente el tamaño de almacenamiento dinámico de VM usando dalvik.system.VMRuntime.setMinimumHeapSize (), lo cual 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();
}
TalkLittle
fuente
3
puedes crear generaciones como generación dura y generación suave. puede arreglar un tiempo para borrar la memoria caché borrará todas las imágenes a las que no se accedió en 3 segundos ... puede echar un vistazo al proyecto de Google
Shelves
developer.android.com/reference/java/lang/ref/… SoftReference doc tiene una nota sobre el almacenamiento en caché, consulte la sección "Evitar referencias suaves para el almacenamiento en caché". La mayoría de las aplicaciones deberían usar un android.util.LruCache en lugar de referencias suaves.
vokilam
Admiro su código, pero ahora en el nuevo Android O / S hay recolección de basura 'agresiva'. Tener una referencia débil no tiene ningún sentido para mí.
j2emanue
@ j2emanue Tienes razón, como traté de indicar en la parte superior de mi respuesta, las SoftReferences son basura recolectada demasiado rápido. Trataré de editar esta respuesta para hacerlo aún más claro.
TalkLittle
97

Picasso

Utilice la biblioteca Picasso de Jake Wharton. (Una biblioteca perfecta de ImageLoading del desarrollador de ActionBarSherlock)

Una potente biblioteca de descarga y almacenamiento en caché de imágenes para Android.

Las imágenes agregan un contexto muy necesario y un toque visual 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 muchas trampas comunes de carga de imágenes en Android:

Manejo del reciclaje de ImageView y cancelación de descarga en un adaptador. Transformaciones de imagen complejas con un uso mínimo de memoria. Memoria automática y almacenamiento en 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, el almacenamiento en caché de memoria y disco, y la agrupación de recursos en una interfaz simple y fácil de usar.

Glide admite la obtención, 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 forma predeterminada, Glide utiliza una pila basada en HttpUrlConnection personalizada, pero también incluye bibliotecas de utilidades conectadas al proyecto Volley de Google o la biblioteca OkHttp de Square.

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

El enfoque 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.

Biblioteca de carga de imágenes Glide

Fresco de Facebook

Fresco es un poderoso sistema para mostrar imágenes en aplicaciones de Android.

Fresco se encarga de la carga y visualización de imágenes, para que no tenga que hacerlo. Cargará imágenes de la red, el almacenamiento local o los recursos locales, y mostrará un marcador de posición hasta que llegue la imagen. Tiene dos niveles de caché; uno en memoria y otro en almacenamiento interno.

Fresco Github

En Android 4.xy versiones anteriores, Fresco coloca 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.

Documentación del fresco

Ashwin S Ashok
fuente
Picasso es una biblioteca desarrollada por Square
LordRaydenMK
83

Cargador de alto rendimiento: después de examinar los métodos sugeridos aquí, utilicé la solución de Ben con algunos cambios:

  1. Me di cuenta de que trabajar con drawables es más rápido que con mapas de bits, así que en su lugar uso drawables

  2. El uso de SoftReference es excelente, pero hace que la imagen en caché se elimine con demasiada frecuencia, por lo que agregué una lista vinculada que contiene referencias de imágenes, evitando que la imagen se elimine, hasta que alcanzó un tamaño predefinido

  3. Para abrir InputStream, utilicé java.net.URLConnection, que me permite usar el caché web (primero debe configurar un 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;
}
}
Pinhassi
fuente
¡Funciona genial! Por cierto, hay un error tipográfico en el nombre de la clase.
Mullins
55
En caso de que ahorre tiempo a otra persona: 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;
Michael Reed
Muchas gracias, esta es una buena implementación. También puse un marcador de posición diferente para cuando se carga el sorteo para que el usuario pueda obtener algunos comentarios.
Juan Hernández
También creo que es mejor usar una cola LIFO en el ejecutorService (mThreadPool) en lugar del FIFO predeterminado para que las últimas imágenes solicitadas (que probablemente sean las visibles) se carguen primero. Ver stackoverflow.com/questions/4620061/how-to-create-lifo-executor
Juan Hernández
99
@MichaelReed, en caso de que sea un usuario de Eclipse, le recomiendo usar Ctrl-Shift-O (esa es la letra O, no el número 0). Automatiza el proceso de agregar importaciones y las organiza por usted. Si estás en una Mac, usa Command-Shift-O en su lugar.
SilithCrowe
78

He seguido esta capacitación en Android y creo que hace un excelente trabajo al descargar imágenes sin bloquear la interfaz de usuario principal. También maneja el almacenamiento en caché y el desplazamiento por muchas imágenes: carga de mapas de bits grandes de manera eficiente

toobsco42
fuente
Lo siento, solo señalé una sola clase para la aplicación Google IO (y llego demasiado tarde para editar). Realmente debería estudiar todas sus clases de utilidad de carga y almacenamiento en caché de imágenes que puede encontrar en el mismo paquete que la clase de caché .
mkuech
¿Alguien recomendaría tomar los archivos DiskLruCache, Image * .java de la carpeta de utilidades de la aplicación iosched para ayudar a manejar la carga / almacenamiento en caché de imágenes para la vista de lista? Quiero decir que definitivamente vale la pena seguir las guías para desarrolladores en línea sobre el tema, pero estas clases (de iosched) van un poco más allá con el patrón.
Gautam
65

1. Picasso permite la carga de imágenes sin problemas en su aplicación, ¡a menudo en una línea de código!

Utiliza Gradle:

implementation 'com.squareup.picasso:picasso:2.71828'

¡Solo 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 centrada en el desplazamiento suave

Utiliza 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 imágenes, para que no tenga que hacerlo.

Comenzando con Fresco

chiragkyada
fuente
Este tutorial puede ayudarlo más para PICASOO: - androidtutorialshub.com/… y GLIDE: - androidtutorialshub.com/…
lalit vasan
52

He escrito un tutorial que explica cómo hacer una carga lenta de imágenes en una vista de lista. Entro en algunos detalles sobre los temas de reciclaje y concurrencia. También uso un grupo de subprocesos fijos para evitar que se generen muchos subprocesos.

Carga lenta de imágenes en Listview Tutorial

Ben Ruijl
fuente
41

La forma en que lo hago es iniciando un hilo para descargar las imágenes en segundo plano y entregarle una devolución de llamada para cada elemento de la lista. Cuando termina de descargar una imagen, llama a la devolución de llamada que actualiza la vista del elemento de la lista.

Sin embargo, este método no funciona muy bien cuando recicla vistas.

jasonhudgins
fuente
También utilizo un hilo para cada imagen. Si separa su modelo de su vista, puede mantener el modelo fuera de la Actividad (como en su clase de 'aplicación') para mantenerlos en caché. Tenga cuidado de quedarse sin recursos si tiene muchas imágenes.
James A Wilson
¿Puedes por favor elaborar? Soy nuevo en el desarrollo de Android. Sin embargo
lostIn tránsito
14
Iniciar un nuevo hilo para cada imagen no es una solución efectiva. Puede terminar con muchos subprocesos en la memoria y congelar la IU.
Fedor
Fedor, de acuerdo, usualmente uso una cola y un grupo de subprocesos, esa es la mejor manera de hacerlo.
jasonhudgins
29

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:

  • De código abierto, pero dirigida por Jake Whartonde ActionBarSherlock fama.
  • Cargue imágenes de forma asíncrona desde la red o los recursos de la aplicación con una línea de código
  • Automático ListViewDetección
  • Disco automático y almacenamiento en memoria caché
  • Puede hacer transformaciones personalizadas
  • Muchas opciones configurables
  • API super simple
  • Actualizado frecuentemente
howettl
fuente
29

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 nuevas aplicaciones de Google. Definitivamente vale la pena echarle un vistazo.

Droidment
fuente
1
Creo que esta es la mejor solución - las otras respuestas son muy antiguas - el voley es realmente rápido y combinado con jake warthons disklrucache es una solución perfecta - probé muchos otros pero ninguno es estable y rápido como el voley
Alexander Sidikov Pfeif
26

Bueno, el tiempo de carga de imágenes desde Internet tiene muchas soluciones. También puede usar la biblioteca Android-Query . Te dará toda la actividad requerida. Asegúrese de lo que quiere hacer y lea la página wiki de la biblioteca. Y resuelva la restricción de carga de imágenes.

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;
}

Debería resolver su problema de carga diferida.

Rahul Rawat
fuente
Buen trabajo para mí, pero necesita un archivo Jar para incluir en su proyecto. Puede descargar ese archivo JAR desde aquí AQuery androidAQuery = new AQuery (this); link Is: code.google.com/archive/p/android-query/downloads
Selim Raza
25

Creo que este problema es muy popular entre los desarrolladores de Android, y hay muchas bibliotecas de este tipo que afirman resolver este problema, pero solo algunas de ellas parecen estar en la marca. AQuery es una de esas bibliotecas, pero es mejor que la mayoría de ellas en todos los aspectos y vale la pena intentarlo.

Ritesh Kumar Dubey
fuente
22

Debe probar este cargador universal es lo mejor. Estoy usando esto después de haber hecho muchos RnD en la carga diferida.

Cargador universal de imágenes

Caracteristicas

  • Carga de imágenes multiproceso (asíncrono o sincronización)
  • Amplia personalización de la configuración de ImageLoader (ejecutores de subprocesos, descargador, decodificador, memoria y caché de disco, opciones de visualización de imágenes, etc.)
  • Muchas opciones de personalización para cada llamada de imagen de pantalla (imágenes de código auxiliar, interruptor de almacenamiento en caché, opciones de decodificación, procesamiento y visualización de mapas de bits, etc.)
  • Almacenamiento en caché 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 descarga)

Soporte para Android 2.0+

ingrese la descripción de la imagen aquí

Patel Girish
fuente
20

Eche un vistazo a Shutterbug , el ligero puerto SDWebImage (una buena biblioteca en iOS) de Applidium para Android. Admite almacenamiento en caché asíncrono, almacena URL fallidas, maneja bien la concurrencia y se incluyen subclases útiles.

¡Las solicitudes de extracción (y los informes de errores) también son bienvenidas!

PatrickNLT
fuente
16

DroidParts tiene ImageFetcher que requiere cero configuración para comenzar.

  • Utiliza un disco y caché en memoria menos utilizado recientemente (LRU).
  • Decodifica eficientemente las imágenes.
  • Admite la modificación de mapas de bits en el hilo de fondo.
  • Tiene simple fundido cruzado.
  • Tiene devolución de llamada de progreso de carga de imagen.

Clone DroidPartsGram para un ejemplo:

Ingrese la descripción de la imagen aquí

Yanchenko
fuente
Hola, he echado un vistazo a los ejemplos de código, pero tengo problemas para usar ImageFetcher con un ArrayAdapter, ¿te importaría leer mi pregunta? stackoverflow.com/questions/21089147/… Gracias =]
masha
16

Solo un consejo rápido para alguien que no está decidido con respecto a qué biblioteca usar para las imágenes de carga lenta:

Hay cuatro formas básicas.

  1. Bricolaje => No es la mejor solución, pero para algunas imágenes y si quieres ir sin la molestia de usar otras bibliotecas

  2. Volley's Lazy Loading library => De chicos de android. Es agradable y todo, pero está mal documentado y, por lo tanto, es un problema de uso.

  3. Picasso: una solución simple que simplemente funciona, incluso puede especificar el tamaño exacto de la imagen que desea incorporar. Es muy simple de usar, pero puede que no sea muy "eficaz" para aplicaciones que tienen que lidiar con enormes cantidades de imágenes.

  4. 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 de imágenes asíncronas más madura que he visto hasta ahora.

Bijay Koirala
fuente
15

Novoda también tiene una gran biblioteca de carga de imágenes perezosas y muchas aplicaciones como Songkick, Podio, SecretDJ e ImageSearch usan su biblioteca.

Su biblioteca está alojada aquí 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.

Soham
fuente
1
En realidad, Novoda es una gran biblioteca, pero ... a veces no necesitas una gran biblioteca y solo un enfoque simple de la solución. Es por eso que LazyList en Github es tan bueno, si sus aplicaciones solo muestran una imagen en listView y no es la característica principal de su aplicación, solo otra actividad que preferiría usar algo más ligero. De lo contrario, si sabe que tendrá que usarlo con frecuencia y es parte del núcleo, pruebe Novoda.
Nicolas Jafelle
13

Mira mi tenedor de LazyList . Básicamente, mejoro LazyList retrasando la llamada de ImageView y creo dos métodos:

  1. Cuando necesitas poner algo como "Cargando imagen ..."
  2. Cuando necesite mostrar la imagen descargada.

También mejoré el ImageLoader al implementar un singleton en este objeto.

Nicolas Jafelle
fuente
12

Si desea mostrar el diseño Shimmer como Facebook, hay una biblioteca oficial de Facebook para eso. FaceBook Shimmer Android

Se encarga de todo, solo necesita poner el código de diseño deseado de manera anidada en un marco brillante. Aquí hay un código de muestra.

<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 de Java para ello.

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

Agregue esta dependencia en su archivo gradle.

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

Así es como se ve.Captura de pantalla de Android Shimmer

Zankrut Parmar
fuente
11

Todos los códigos anteriores tienen su propio valor, pero con mi experiencia personal, solo pruebe con Picasso.

Picasso es una biblioteca específicamente para este propósito, de hecho administrará la memoria caché y todas las demás operaciones de red automáticamente. Tendrá que agregar una biblioteca en su proyecto y simplemente escribir una sola 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

AKber
fuente
9

Usa la biblioteca de planeo. Funcionó para mí y también funcionará para su 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);
}
Saket Kumar
fuente
7

Puedo recomendar una forma diferente que funciona como un encanto: Android Query.

Puede descargar ese archivo JAR desde aquí

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 usar esto puedes encontrar muchas más funciones como la animación al cargar, obtener un mapa de bits (si es necesario), etc.

Pratik Dasa
fuente
7

Prueba Aquery . Tiene métodos sorprendentemente simples para cargar y almacenar en caché las imágenes de forma asincrónica.

usuario2779311
fuente
4
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;
 }
}
Nikhil Gupta
fuente
4

Tuve este problema e implementé lruCache. Creo que necesita la API 12 y 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 eso, puede usar un caché de disco ... Todo se describe en Caching Bitmaps .

Ahora proporcionaré mi implementación, que es un singleton al que llamo desde cualquier lugar como este:

//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);
        }
    }
}
j2emanue
fuente