Pregunta Carga lenta de imágenes en ListView


Estoy usando un ListView para mostrar algunas imágenes y leyendas asociadas con esas imágenes. Obtengo las imágenes de Internet. ¿Hay alguna forma de cargar las imágenes de forma perezosa, de modo que mientras se muestra el texto, la IU no está bloqueada y las imágenes se muestran a medida que se descargan?

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


1731
2018-02-12 15:59


origen


Respuestas:


Esto es lo que creé para guardar 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();
    }
}

1015
2018-02-18 03:56



hice una demostración simple de una lista perezosa (ubicado 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 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 vistas recicladas correctamente.

Alt text


982
2018-06-18 08:04



Recomiendo el instrumento de código abierto Universal Image Loader. Originalmente se basa en el proyecto de Fedor Vlasov Lista de espera y ha sido ampliamente mejorado desde entonces.

  • Carga de imágenes multiproceso
  • Posibilidad de ajuste amplio de la configuración de ImageLoader (ejecutores de subprocesos, downlaoder, decodificador, caché de memoria y 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
  • Compatibilidad con widgets
  • Soporte para Android 2.0+


530
2017-12-19 13:53



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, limitado, FIFO cache.
  • Un suave y fácil garbage collect-ed caché.
  • UN marcador de posición  Drawable mientras descargas.

enter image description here


147
2017-08-12 11:07



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 cambio, prueba con 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. Lamentablemente, SoftReferences causó que mis imágenes se recogieran basura demasiado rápido. 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 SoftReferences en grupos de google: enlace al hilo. Como solución a la recolección de basura demasiado temprana, sugieren la posibilidad de establecer manualmente el tamaño del almacenamiento dinámico de la máquina virtual utilizando 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();
}

102
2018-05-05 13:16



Picasso 

Utiliza la Biblioteca Picasso de Jake Wharton. (Una biblioteca Perfect ImageLoading es el 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 cargar imágenes sin inconvenientes 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);

Muchas de las trampas comunes de la carga de imágenes en Android son manejadas automáticamente por Picasso:

Manejo del reciclaje de ImageView y cancelación de descarga en un adaptador. Transformaciones de imagen complejas con uso mínimo de memoria. Memoria automática y almacenamiento en caché de disco.

Biblioteca de Picasso Jake Wharton

Planeo

Glide es un marco de gestión de medios de código abierto rápido y eficiente para Android que integra 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 búsqueda, la decodificación y la visualización de imágenes fijas, videos y GIF animados. Glide incluye una API flexible que permite a los desarrolladores conectarse a casi cualquier pila de red. Por defecto, Glide usa una pila basada en HttpUrlConnection personalizada, pero también incluye bibliotecas de utilidades que se conectan 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 sea lo más fácil 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.

Glide Image Loading Library

Fresco por Facebook 

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

Fresco se encarga de la carga y visualización de la imagen, por lo que no es necesario. 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.

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 Fresco


84
2018-04-04 12:35



Cargador de alto rendimiento: después de examinar los métodos sugeridos aquí, solía La solución de Ben con algunos cambios -

  1. Me di cuenta de que trabajar con herramientas arrastrables es más rápido que con mapas de bits, así que utilizo herramientas deslizables en su lugar

  2. Usar 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, evitando que la imagen sea eliminada, hasta que alcanzó un tamaño predefinido

  3. Para abrir el InputStream utilicé java.net.URLConnection, que me permite usar el caché web (primero debe establecer un caché de respuestas, 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;
}
}

77
2017-12-27 23:27



Seguí este entrenamiento de 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 se ocupa de desplazarse por muchas imágenes: Cargando grandes mapas de bits de manera eficiente


75
2018-05-22 06:00