Domanda Caricamento lento delle immagini in ListView


Sto usando un ListView per visualizzare alcune immagini e didascalie associate a quelle immagini. Sto ottenendo le immagini da Internet. C'è un modo per caricare le immagini in modo pigro così mentre il testo viene visualizzato, l'interfaccia utente non è bloccata e le immagini vengono visualizzate mentre vengono scaricate?

Il numero totale di immagini non è fisso.


1731
2018-02-12 15:59


origine


risposte:


Ecco cosa ho creato per contenere le immagini che la mia app sta attualmente visualizzando. Si noti che l'oggetto "Log" in uso qui è il mio wrapper personalizzato attorno alla classe Log finale all'interno di 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



ho fatto una semplice demo di una lista pigra (situato in GitHub) con immagini. Potrebbe essere utile a qualcuno. Scarica immagini nel thread in background. Le immagini vengono memorizzate nella cache su una scheda SD e in memoria. L'implementazione della cache è molto semplice ed è sufficiente per la demo. Decodifico le immagini con inSampleSize per ridurre il consumo di memoria. Cerco anche di gestire correttamente le viste riciclate.

Alt text


982
2018-06-18 08:04



Raccomando lo strumento open source Universal Image Loader. È originariamente basato sul progetto di Fedor Vlasov Lista pigra ed è stato notevolmente migliorato da allora.

  • Caricamento immagine multithread
  • Possibilità di configurazione avanzata della configurazione di ImageLoader (thread executor, downlaoder, decoder, memoria e cache del disco, opzioni di visualizzazione delle immagini e altro)
  • Possibilità di memorizzare la cache delle immagini nella memoria e / o sul file sysytem del dispositivo (o sulla scheda SD)
  • Possibilità di "ascoltare" il processo di caricamento
  • Possibilità di personalizzare ogni chiamata di immagini display con opzioni separate
  • Supporto per i widget
  • Supporto per Android 2.0+


530
2017-12-19 13:53



Multithreading per prestazioni, un tutorial di Gilles Debunne.

Questo è dal blog degli sviluppatori Android. Il codice suggerito utilizza:

  • AsyncTasks.
  • Una dimensione difficile, limitata, FIFO cache.
  • Un morbido, facilmente garbage collect-ed cache.
  • UN segnaposto  Drawable mentre scarichi.

enter image description here


147
2017-08-12 11:07



Aggiornamento: nota che questa risposta è piuttosto inefficace ora. Il Garbage Collector agisce in modo aggressivo su SoftReference e WeakReference, quindi questo codice NON è adatto per nuove app.  (Invece, prova le librerie come Universal Image Loader suggerito in altre risposte).

Grazie a James per il codice e Bao-Long per il suggerimento di usare SoftReference. Ho implementato le modifiche di SoftReference sul codice di James. Sfortunatamente SoftReferences ha causato la raccolta delle mie immagini troppo rapidamente. Nel mio caso andava bene senza le cose di SoftReference, perché la mia lista di dimensioni è limitata e le mie immagini sono piccole.

C'è una discussione di un anno fa riguardante le SoftReferences su gruppi di google: collegamento al thread. Come soluzione alla raccolta dei rifiuti troppo precoce, suggeriscono la possibilità di impostare manualmente la dimensione dell'heap della VM utilizzando dalvik.system.VMRuntime.setMinimumHeapSize (), che per me non è molto interessante.

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 

Usa la libreria Picasso di Jake Wharton. (Una libreria Perfect ImageLoading forma lo sviluppatore di ActionBarSherlock)

Una potente libreria di download e memorizzazione di immagini per Android.

Le immagini aggiungono il contesto e il fascino visivo necessari alle applicazioni Android. Picasso consente il caricamento delle immagini senza problemi nell'applicazione, spesso in un'unica riga di codice!

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Molte delle più comuni insidie ​​del caricamento delle immagini su Android sono gestite automaticamente da Picasso:

Gestione del riciclaggio di ImageView e cancellazione del download in un adattatore. Trasformazioni complesse dell'immagine con un utilizzo minimo della memoria. Memoria automatica e memorizzazione nella cache del disco.

Biblioteca di Picasso Jake Wharton

planata

Glide è un framework di gestione dei media open source veloce ed efficiente per Android che avvolge la decodifica dei media, la memoria e il caching del disco e il pooling delle risorse in un'interfaccia semplice e facile da usare.

Glide supporta il recupero, la decodifica e la visualizzazione di fermi immagine, immagini e GIF animate. Glide include una API flessibile che consente agli sviluppatori di collegarsi a quasi tutti gli stack di rete. Per impostazione predefinita, Glide utilizza uno stack basato su HttpUrlConnection personalizzato, ma include anche librerie di utilità plug-in nel progetto Volley di Google o nella libreria OkHttp di Square.

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

L'obiettivo principale di Glide è quello di far scorrere qualsiasi tipo di elenco di immagini il più agevole e veloce possibile, ma Glide è efficace anche in quasi tutti i casi in cui è necessario recuperare, ridimensionare e visualizzare un'immagine remota.

Glide Image Caricamento della libreria

Affresco su Facebook 

Fresco è un potente sistema per la visualizzazione di immagini in applicazioni Android.

Affresco si occupa del caricamento e della visualizzazione delle immagini, quindi non è necessario. Caricherà le immagini dalla rete, dall'archivio locale o dalle risorse locali e visualizzerà un segnaposto finché l'immagine non sarà arrivata. Ha due livelli di cache; uno in memoria e un altro nella memoria interna.

Affresco Github

In Android 4.xe versioni inferiori, Fresco inserisce le immagini in una regione speciale della memoria Android. Ciò consente alla tua applicazione di funzionare più velocemente e subisce il temuto OutOfMemoryError molto meno spesso.

Documentazione dell'affresco


84
2018-04-04 12:35



Caricatore ad alte prestazioni - dopo aver esaminato i metodi suggeriti qui, ero solito La soluzione di Ben con alcune modifiche -

  1. Mi sono reso conto che lavorare con i drawable è più veloce di quello con bitmap, quindi uso invece drawable

  2. L'utilizzo di SoftReference è ottimo, ma rende l'immagine memorizzata nella cache troppo spesso, quindi ho aggiunto un elenco collegato che contiene riferimenti alle immagini, impedendo l'eliminazione dell'immagine, fino a raggiungere una dimensione predefinita

  3. Per aprire InputStream ho usato java.net.URLConnection che mi permette di usare la cache web (devi prima impostare una cache di risposta, ma questa è un'altra storia)

Il mio codice:

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



Ho seguito questo allenamento Android e penso che faccia un ottimo lavoro nel download delle immagini senza bloccare l'interfaccia utente principale. Gestisce anche il caching e si occupa dello scorrimento di molte immagini: Caricamento di bitmap di grandi dimensioni in modo efficiente


75
2018-05-22 06:00



1.  Picasso consente il caricamento delle immagini senza problemi nell'applicazione, spesso in un'unica riga di codice!

Usa Gradle:

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

Solo una riga di codice!

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

2.  planata Una libreria di caricamento e memorizzazione nella cache di immagini per Android focalizzata sullo scorrimento fluido

Usa Gradle:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

// Per una vista semplice:

  Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);

3.  affrescare  è un potente sistema per la visualizzazione di immagini in Android    applicazioni.Fresco si occupa del caricamento e della visualizzazione delle immagini, quindi non hai   a.

Iniziare con Affresco


56
2017-08-27 12:54



Ho scritto un tutorial che spiega come eseguire il caricamento lazy delle immagini in un listview. Vado in alcuni dettagli sulle questioni del riciclaggio e della concorrenza. Uso anche un pool di thread fisso per evitare di generare molti thread.

Caricamento lento delle immagini in Listview Tutorial


50
2018-02-12 20:07