Como carregar preguiçosamente imagens no ListView no Android

1931

Estou usando um ListViewpara exibir algumas imagens e legendas associadas a essas imagens. Estou recebendo as imagens da Internet. Existe uma maneira de carregar imagens preguiçosamente e, enquanto o texto é exibido, a interface do usuário não é bloqueada e as imagens são exibidas durante o download?

O número total de imagens não é fixo.

lostInTransit
fonte
10
Você pode usar o AsyncImageView do GreenDroid . Basta ligar setUrl.
Pascal Dimassimo 9/12
7
Eu usei isso. É uma implementação maravilhosa. Más notícias de que o AsyncImageView faz parte de um grande projeto GreenDroid, que torna seu aplicativo maior, mesmo no caso de tudo o que você precisa é o AsyncImageView. Além disso, parece, projeto GreenDroid não é atualizado desde 2011.
borisstr
5
Você pode até tentar esta biblioteca: Android-http-image-manager, que na minha opinião é o melhor para carregamento assíncrono de imagens.
Ritesh Kumar Dubey
34
Basta usar o Picasso, ele fará tudo sozinho. 'Picasso.with (yourContext) .load (img src / path / drawable here) .into (imageView, por exemplo, seu destino);' É isso aí!
Anuj Sharma
6
tente usar: github.com/nostra13/Android-Universal-Image-Loader , esta biblioteca é muito rápido e eficiente para o carregamento lento e cache de imagem
Krunal patel

Respostas:

1087

Aqui está o que eu criei para armazenar as imagens que meu aplicativo está exibindo no momento. Observe que o objeto "Log" em uso aqui é meu invólucro personalizado em torno da classe final do Log dentro do 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
fonte
104
Eu acho que você deve usar SoftReferences para que seu programa nunca cause OutOfMemoryException. Como o GC pode limpar as softreferences quando o tamanho da pilha está aumentando ... você pode gerenciar sua própria geração, após alguns segundos você pode colocar suas imagens nessa lista e antes de carregar, verifique se a imagem existe, não faça o download novamente. -lo a partir dessa lista e também colocá-lo de volta à sua lista softref e depois de algum tempo você pode limpar seu hardlist :)
AZ_
36
O projeto Google Shelves é um excelente exemplo de como eles funcionaram
Code.google.com/p/shelves
12
Você não perde um retorno quando drawableMap contém a imagem ... sem iniciar o segmento de busca?
28411 Karussell
5
Este código tem vários problemas. Primeiramente, você deve armazenar em cache Drawables, o que causará um vazamento de memória: stackoverflow.com/questions/7648740/… . Em segundo lugar, o cache em si nunca é limpo e, portanto, cresce para sempre, isso é outro vazamento de memória.
satur9nine
10
ninguém ouviu falar sobre LRU Cache developer.android.com/training/displaying-bitmaps/…
Muhammad Babar
1025

Fiz uma demonstração simples de uma lista lenta (localizada no GitHub) com imagens.

Uso básico

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

Não se esqueça de adicionar as seguintes permissões ao seu AndroidManifest.xml:

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

crie apenas uma instância do ImageLoader e reutilize-a em todo o aplicativo. Dessa forma, o cache de imagens será muito mais eficiente.

Pode ser útil para alguém. Ele baixa imagens no segmento de segundo plano. As imagens estão sendo armazenadas em cache no cartão SD e na memória. A implementação do cache é muito simples e é suficiente para a demonstração. Decodifico imagens com o inSampleSize para reduzir o consumo de memória. Também tento lidar com visualizações recicladas corretamente.

texto alternativo

Fedor
fonte
7
Obrigado, estou usando uma versão ligeiramente modificada do seu código em quase toda parte do meu projeto: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/…
shaman.sir
3
Alguém já viu o código de Gilles Debunne como uma alternativa. As duas grandes diferenças que vejo são 1) no cache da memória versus cartão SD e 2) no uso de AsyncTasks em vez de em um pool de threads. android-developers.blogspot.com/2010/07/…
Richard
4
Há um erro, às vezes é acionado: 10-13 09: 58: 46.738: ERROR / AndroidRuntime (24250): Manipulador não capturado: saída principal do thread devido à exceção não capturada 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: índice da matriz fora do intervalo: 0 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): em java.util.Vector.elementAt (Vector.java:331) 10-13 09: 58: 46.768: ERRO / AndroidRuntime (24250): em java.util.Vector.get (Vector.java:445) 10-13 09: 58: 46.768: ERRO / AndroidRuntime (24250): em com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java:91)
Mikooos
64
Gostaria de adicionar aos iniciantes como eu que, se você quiser adicionar esse código ao seu próprio projeto, você deve adicionar permissões de cache externo no manifesto. Obrigado
Adam
2
@BruceHill Não há photosQueue no código-fonte mais recente. Você parece estar usando uma versão muito antiga.
Fedor
552

Eu recomendo o Universal Image Loader de instrumento de código aberto . Ele é originalmente baseado no projeto LazyList, de Fedor Vlasov, e foi amplamente aprimorado desde então.

  • Carregamento de imagens multithread
  • Possibilidade de ajuste amplo da configuração do ImageLoader (executores de threads, downlaoder, decodificador, cache de memória e disco, opções de exibição de imagem e outros)
  • Possibilidade de cache de imagens na memória e / ou no sistema de arquivos do dispositivo (ou cartão SD)
  • Possibilidade de "ouvir" o processo de carregamento
  • Possibilidade de personalizar todas as chamadas de imagens do display com opções separadas
  • Suporte de widget
  • Suporte para Android 2.0 ou superior

nostra13
fonte
16
se você está procurando um ótimo código de "carregamento de imagem" que o criador trate e mantenha como seu bebê precioso (não apenas uma solução única), você acabou de encontrá-lo; Nostra
AndroidGecko 29/03/2012
Realmente um excelente trabalho, mas fica um pouco atrasado na minha lista. Se você pode dizer a melhor performance do compilador 'ImageLoader' ... ou talvez seja porque o 'DisplayImageOptions'.
Dinit #
Eu amo muito esse gridview, mas não posso usá-lo no meu tutorial. Eu sou um iniciante no Android. Eu faço aplicação sobre o gridview, mas meu aplicativo era targetSdkVersion 15, portanto, preciso usar o Asynctask para processo no thread em segundo plano. Eu costumava usar esse gridview, mas não funciona. como posso usá-lo na targetSdkVersion 15?
Kongkea
Você pode criar uma pergunta separada com tag [-image-carregador universal]
nostra13
3
Depois de ler os documentos oficiais do Android e tentar fazer essas coisas sozinho, tenho certeza de que essa biblioteca é o fim de tudo carregar imagens. Não é possível votar o suficiente.
Core
159

Multithreading For Performance , um tutorial de Gilles Debunne.

Isso é do Blog dos desenvolvedores do Android. O código sugerido usa:

  • AsyncTasks.
  • Um tamanho rígido e limitado FIFO cache.
  • Um garbage collectcache macio e de fácil instalação .
  • Um espaço reservado Drawable enquanto você baixa.

insira a descrição da imagem aqui

Thomas Ahle
fonte
10
Também funciona bem na versão 2.1. Só não use o AndroidHttpClient.
Thomas Ahle
2
@ thomas-ahle Obrigado, eu vi o AndroidHttpClient estar dando erros no 2.1, como é implementado no 2.2, mas realmente não tentei encontrar outra coisa para substituí-lo.
Adinia 9/02/11
4
@ Adina Você está certo, eu esqueci. No entanto, não há nada na receita que não possa ser feito com um HttpClient normal.
Thomas Ahle
Ouvi em vários lugares que o Google não recomenda referências flexíveis porque o kernel do Android está muito ansioso para coletar essas referências em comparação com as versões anteriores do sistema.
Muhammad Ahmed AbuTalib
109

Atualização: observe que esta resposta é bastante ineficaz agora. O Garbage Collector atua de forma agressiva no SoftReference e no WeakReference, portanto esse código NÃO é adequado para novos aplicativos. (Em vez disso, tente bibliotecas como o Universal Image Loader sugeridas em outras respostas.)

Agradecemos a James pelo código e a Bao-Long pela sugestão de usar o SoftReference. Eu implementei as alterações do SoftReference no código de James. Infelizmente, o SoftReferences fez com que minhas imagens fossem coletadas como lixo muito rapidamente. No meu caso, não houve problemas com o SoftReference, porque meu tamanho de lista é limitado e minhas imagens são pequenas.

Há uma discussão de um ano atrás sobre as SoftReferences nos grupos do google: link to thread . Como solução para a coleta de lixo muito cedo, eles sugerem a possibilidade de definir manualmente o tamanho de heap da VM usando dalvik.system.VMRuntime.setMinimumHeapSize (), o que não é muito atraente para mim.

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
fonte
3
você pode criar gerações como geração pesada e geração suave. você pode corrigir um cache claro o tempo vai apagar todas as imagens que não foram acessados em 3 seg .. você pode ter um olhar para as prateleiras projeto Google
AZ_
O documento developer.android.com/reference/java/lang/ref/… SoftReference possui uma observação sobre armazenamento em cache, consulte a seção "Evitar referências suaves para armazenamento em cache". A maioria dos aplicativos deve usar um android.util.LruCache em vez de referências suaves.
vokilam
Admiro seu código, mas agora no novo Android O / S há uma coleta de lixo 'agressiva'. Manter uma referência fraca não faz sentido para mim.
J2emanue
@ j2emanue Você está certo, como tentei indicar na parte superior da minha resposta, as SoftReferences são lixo coletado muito rapidamente. Vou tentar editar esta resposta para tornar isso ainda mais claro.
TalkLittle
97

Picasso

Use a Biblioteca Picasso de Jake Wharton. (Uma perfeita biblioteca ImageLoading do desenvolvedor do ActionBarSherlock)

Uma poderosa biblioteca de download e cache de imagens para Android.

As imagens adicionam contexto e talento visual necessários aos aplicativos Android. O Picasso permite o carregamento de imagens sem problemas no seu aplicativo - geralmente em uma linha de código!

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

Muitas armadilhas comuns do carregamento de imagens no Android são tratadas automaticamente pelo Picasso:

Manuseando a reciclagem do ImageView e cancelando o download em um adaptador. Transformações de imagem complexas com uso mínimo de memória. Memória automática e cache de disco.

Biblioteca de Picasso Jake Wharton

Deslizar

O Glide é uma estrutura de gerenciamento de mídia de código aberto rápida e eficiente para Android que agrupa decodificação de mídia, cache de memória e disco e pool de recursos em uma interface simples e fácil de usar.

O Glide suporta a busca, decodificação e exibição de vídeos, imagens e GIFs animados. O Glide inclui uma API flexível que permite que os desenvolvedores se conectem a praticamente qualquer pilha de rede. Por padrão, o Glide usa uma pilha personalizada baseada em HttpUrlConnection, mas também inclui bibliotecas de utilitários conectadas ao projeto Volley do Google ou a biblioteca OkHttp da Square.

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

O foco principal do Glide é tornar a rolagem de qualquer tipo de lista de imagens o mais suave e rápida possível, mas o Glide também é eficaz para quase todos os casos em que você precisa buscar, redimensionar e exibir uma imagem remota.

Biblioteca de carregamento de imagens Glide

Fresco pelo Facebook

Fresco é um sistema poderoso para exibir imagens em aplicativos Android.

O Fresco cuida do carregamento e exibição da imagem, para que você não precise. Ele carrega imagens da rede, armazenamento local ou recursos locais e exibe um espaço reservado até a imagem chegar. Possui dois níveis de cache; um na memória e outro no armazenamento interno.

Fresco Github

No Android 4.xe inferior, o Fresco coloca as imagens em uma região especial da memória do Android. Isso permite que seu aplicativo seja executado mais rapidamente - e sofra o temido OutOfMemoryError com muito menos frequência.

Documentação Fresco

Ashwin S Ashok
fonte
Picasso é uma biblioteca desenvolvido pela Square
LordRaydenMK
83

Carregador de alto desempenho - depois de examinar os métodos sugeridos aqui, usei a solução de Ben com algumas alterações -

  1. Eu percebi que trabalhar com drawables é mais rápido que com bitmaps, então eu uso drawables

  2. O uso do SoftReference é ótimo, mas faz com que a imagem em cache seja excluída com muita frequência; por isso, adicionei uma lista vinculada que contém referências de imagens, impedindo que a imagem seja excluída, até atingir um tamanho predefinido.

  3. Para abrir o InputStream, usei java.net.URLConnection, que me permite usar o cache da web (você precisa definir um cache de resposta primeiro, mas isso é outra história)

Meu 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
fonte
Funciona bem! Entre, há um erro de digitação no nome da classe.
Mullins
5
Caso isso economize o tempo de outra pessoa: 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
Muito obrigado, esta é uma boa implementação. Também coloquei um espaço reservado diferente para quando o drawable estiver sendo carregado, para que o usuário possa receber algum feedback.
Juan Hernandez
Também acho que é melhor usar uma fila LIFO no executorService (mThreadPool) em vez do FIFO padrão para que as últimas imagens solicitadas (que provavelmente são as visíveis) sejam carregadas primeiro. Veja stackoverflow.com/questions/4620061/how-to-create-lifo-executor
Juan Hernandez
9
@ MichaelReed, caso você seja um usuário do Eclipse, recomendo usar Ctrl-Shift-O (essa é a letra O, não o número 0). Ele automatiza o processo de adição de importações e as organiza para você. Se você estiver em um Mac, use Command-Shift-O.
21412 SilithCrowe
78

Eu segui este treinamento do Android e acho que ele faz um excelente trabalho no download de imagens sem bloquear a interface do usuário principal. Ele também lida com o cache e com a rolagem de muitas imagens: Carregamento de bitmaps grandes com eficiência

toobsco42
fonte
Sinto muito, apenas apontei para uma única classe para o aplicativo Google IO (e estou muito atrasado para editar). Você realmente deve estudar todas as classes de utilitários de carregamento e armazenamento de imagens que podem ser encontradas no mesmo pacote que a classe de cache .
Mckech
Alguém recomendaria pegar os arquivos DiskLruCache, Image * .java da pasta util do aplicativo iosched para ajudar a lidar com o carregamento / armazenamento de imagens em cache para exibição em lista? Quero dizer, definitivamente vale a pena seguir os guias do desenvolvedor on-line sobre o assunto, mas essas classes (da iosched) vão um pouco mais longe com o padrão.
Gautam
65

1. O Picasso permite o carregamento de imagens sem problemas no seu aplicativo - geralmente em uma linha de código!

Use Gradle:

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

Apenas uma linha de código!

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

2. Planar Uma biblioteca de carregamento e armazenamento de imagens para Android focada em rolagem suave

Use Gradle:

repositories {
  mavenCentral() 
  google()
}

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

// Para uma visão simples:

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

3. fresco é um sistema poderoso para a exibição de imagens em aplicativos Android . O Fresco cuida do carregamento e exibição da imagem, para que você não precise.

Introdução ao Fresco

chiragkyada
fonte
Este tutorial pode ajudá-lo mais para PICASOO: - androidtutorialshub.com/... e GLIDE: - androidtutorialshub.com/...
lalit Vasan
52

Eu escrevi um tutorial que explica como fazer o carregamento lento de imagens em uma lista. Entro em alguns detalhes sobre os problemas de reciclagem e concorrência. Eu também uso um pool de threads fixo para evitar a geração de muitos threads.

Carregamento lento de imagens no Tutorial do Listview

Ben Ruijl
fonte
41

A maneira como faço isso é iniciando um thread para baixar as imagens em segundo plano e entregá-lo um retorno de chamada para cada item da lista. Quando o download de uma imagem termina, ele chama o retorno de chamada, que atualiza a exibição do item da lista.

Esse método não funciona muito bem quando você está reciclando visualizações.

jasonhudgins
fonte
usar um thread para cada imagem é a abordagem que eu uso também. Se você separar seu modelo da sua visualização, poderá persistir o modelo fora da Activity (como na classe 'application') para mantê-los em cache. Cuidado para ficar sem recursos se você tiver muitas imagens.
James A Wilson
você pode por favor elaborar. Eu sou novo no desenvolvimento android. Obrigado pelas dicas, porém
lostInTransit
14
Iniciar um novo thread para cada imagem não é uma solução eficaz. Você pode acabar com muitos threads na memória e congelando a interface do usuário.
Fedor
Fedor, concordou, eu costumo usar uma fila e um pool de threads, esse é o melhor caminho a seguir.
Jasonhudgins
32

Eu só quero adicionar mais um bom exemplo, XML Adapters . Como é usado pelo Google, também estou usando a mesma lógica para evitar um erro OutOfMemory.

Basicamente, este ImageDownloader é a sua resposta (pois cobre a maioria dos seus requisitos). Alguns você também pode implementar nisso.

Arslan Anwar
fonte
2
A classe ImageDownloader não é cumprida: consulte a solução abaixo code.google.com/p/parleys-android-nextgen/issues/detail?id=1
Sam
29

Este é um problema comum no Android que foi resolvido de várias maneiras por muitas pessoas. Na minha opinião, a melhor solução que eu já vi é a relativamente nova biblioteca chamada Picasso . Aqui estão os destaques:

  • Código aberto, mas liderado pela fama Jake Whartondo ActionBarSherlock .
  • Carregar imagens de forma assíncrona dos recursos da rede ou do aplicativo com uma linha de código
  • ListViewDetecção automática
  • Armazenamento em cache automático de disco e memória
  • Pode fazer transformações personalizadas
  • Muitas opções configuráveis
  • API super simples
  • Atualizado frequentemente
howettl
fonte
29

Estou usando o NetworkImageView da nova Android Volley Library com.android.volley.toolbox.NetworkImageView, e parece estar funcionando muito bem. Aparentemente, essa é a mesma visualização usada no Google Play e em outros novos aplicativos do Google. Definitivamente vale a pena conferir.

droidment
fonte
1
Acho que esta é a melhor solução - as outras respostas são muito antigas - o vôlei é muito rápido e combinado com jake warthons disklrucache, é uma solução perfeita - tentei muitos outros, mas nenhum é estável e rápido como o vôlei
Alexander Sidikov Pfeif
26

Bem, o tempo de carregamento da imagem na Internet tem muitas soluções. Você também pode usar a biblioteca Android-Query . Isso lhe dará toda a atividade necessária. Certifique-se de que você deseja fazer e leia a página wiki da biblioteca. E resolva a restrição de carregamento da imagem.

Este é o meu 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;
}

Deve resolver o seu problema de carregamento lento.

Rahul Rawat
fonte
Bom trabalho para mim, mas precisa de um arquivo Jar para incluir no seu projeto. Você pode fazer o download desse arquivo JAR aqui AQuery androidAQuery = new AQuery (this); link Is: code.google.com/archive/p/android-query/downloads
Selim Raza
25

Acho que esse problema é muito popular entre os desenvolvedores do Android, e existem muitas dessas bibliotecas que pretendem resolver esse problema, mas apenas algumas delas parecem estar em risco. O AQuery é uma dessas bibliotecas, mas é melhor que a maioria em todos os aspectos e vale a pena tentar.

Ritesh Kumar Dubey
fonte
22

Você deve tentar este Universal Loader é o melhor. Estou usando isso depois de fazer muitos RnD em carregamento lento.

Carregador Universal de Imagens

Recursos

  • Carregamento de imagens multithread (assíncrono ou sincronizado)
  • Ampla personalização da configuração do ImageLoader (executores de threads, downloader, decodificador, cache de memória e disco, opções de imagem de exibição etc.)
  • Muitas opções de personalização para cada chamada de imagem de exibição (imagens stub, comutador de cache, opções de decodificação, processamento e exibição de Bitmap, etc.)
  • Cache de imagem na memória e / ou no disco (sistema de arquivos do dispositivo ou cartão SD)
  • Processo de carregamento de audição (incluindo o progresso do download)

Suporte para Android 2.0 ou superior

insira a descrição da imagem aqui

Girish Patel
fonte
20

Dê uma olhada no Shutterbug , a porta SDWebImage (uma boa biblioteca no iOS) do Applidium para Android. Ele suporta armazenamento em cache assíncrono, armazena URLs com falha, lida bem com a concorrência e subclasses úteis são incluídas.

Pedidos pull (e relatórios de bugs) também são bem-vindos!

PatrickNLT
fonte
16

O DroidParts possui o ImageFetcher que requer configuração zero para começar.

  • Usa um cache LRU ( disco menos usado na memória ).
  • Decodifica imagens com eficiência.
  • Suporta a modificação de bitmaps no thread de segundo plano.
  • Possui cross-fade simples.
  • Possui retorno de chamada de progresso no carregamento de imagem.

Clone o DroidPartsGram por um exemplo:

Digite a descrição da imagem aqui

yanchenko
fonte
Olá, dei uma olhada nos exemplos de código, mas estou tendo problemas ao usar o ImageFetcher com um ArrayAdapter, você se importaria de olhar para a minha pergunta? stackoverflow.com/questions/21089147/… Thanks =]
masha 13/01
16

Apenas uma dica rápida para alguém indeciso sobre qual biblioteca usar para imagens de carregamento lento:

Existem quatro maneiras básicas.

  1. DIY => Não é a melhor solução, mas para algumas imagens e se você quiser ficar sem o incômodo de usar outras bibliotecas

  2. Biblioteca de carregamento preguiçoso do Volley => De caras no android. É bom e tudo, mas está mal documentado e, portanto, é um problema para usar.

  3. Picasso: Uma solução simples que simplesmente funciona, você pode até especificar o tamanho exato da imagem que deseja trazer. É muito simples de usar, mas pode não ser muito "eficiente" para aplicativos que precisam lidar com quantidades enormes de imagens.

  4. UIL: A melhor maneira de carregar imagens com preguiça. Você pode armazenar em cache imagens (é claro que precisa de permissão), inicializar o carregador uma vez e concluir seu trabalho. A biblioteca de carregamento de imagem assíncrona mais madura que eu já vi até agora.

Bijay Koirala
fonte
15

O Novoda também possui uma ótima biblioteca de carregamento de imagens preguiçosas e muitos aplicativos como Songkick, Podio, SecretDJ e ImageSearch usam sua biblioteca.

A biblioteca deles está hospedada aqui no Github e eles também têm um rastreador de problemas bastante ativo . O projeto deles também parece bastante ativo, com mais de 300 confirmações no momento em que escrevemos essa resposta.

Soham
fonte
1
Na verdade, o Novoda é uma ótima biblioteca, mas ... às vezes você não precisa de uma biblioteca enorme e apenas de uma abordagem simples da solução. É por isso que o LazyList no Github é tão bom, se seus aplicativos mostram apenas uma imagem em um listView e não é o principal recurso do seu aplicativo, apenas outra atividade que eu preferiria usar algo mais leve. Caso contrário, se você souber que precisará usar com frequência e faz parte do núcleo, tente o Novoda.
Nicolas Jafelle
13

Verifique meu garfo do LazyList . Basicamente, eu melhoro o LazyList atrasando a chamada do ImageView e crio dois métodos:

  1. Quando você precisa colocar algo como "Carregando imagem ..."
  2. Quando você precisa mostrar a imagem baixada.

Também aprimorei o ImageLoader implementando um singleton neste objeto.

Nicolas Jafelle
fonte
12

Se você deseja exibir o layout Shimmer como o Facebook, existe uma biblioteca oficial do facebook para isso. FaceBook Shimmer Android

Ele cuida de tudo. Você só precisa colocar o código de design desejado de maneira aninhada no quadro shimmer. Aqui está um código de exemplo.

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

E aqui está o código java para ele.

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

Adicione essa dependência ao seu arquivo gradle.

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

Aqui está como ele se parece.Captura de tela do Shimmer Android

Zankrut Parmar
fonte
11

Todo o código acima tem seu próprio valor, mas com a minha experiência pessoal, experimente o Picasso.

Picasso é uma biblioteca especificamente para esse fim, na verdade, ele gerenciará o cache e todas as outras operações de rede automaticamente. Você precisará adicionar uma biblioteca ao seu projeto e apenas escrever uma única linha de código para carregar a imagem do URL remoto.

Por favor, visite aqui: http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149

AKber
fonte
9

Use a biblioteca glide. Ele funcionou para mim e também funcionará para o seu código. Funciona tanto para imagens quanto para gifs também.

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
fonte
7

Posso recomendar uma maneira diferente que funciona como um encanto: Consulta Android.

Você pode baixar esse arquivo JAR aqui

AQuery androidAQuery = new AQuery(this);

Como um exemplo:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

É muito rápido e preciso e, usando isso, você pode encontrar muitos outros recursos, como animação ao carregar, obter um bitmap (se necessário) etc.

Pratik Dasa
fonte
7

Experimente o Aquery . Possui métodos incrivelmente simples para carregar e armazenar em cache imagens de forma assíncrona.

user2779311
fonte
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
fonte
4

Eu tive esse problema e implementei o lruCache. Acredito que você precise da API 12 ou superior ou use a biblioteca v4 compatível. O lurCache é uma memória rápida, mas também possui um orçamento, portanto, se você estiver preocupado com isso, poderá usar um cache de disco ... Está tudo descrito em Caching Bitmaps .

Agora fornecerei minha implementação, que é um singleton que chamo de qualquer lugar como este:

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

Aqui está o código ideal para armazenar em cache e, em seguida, chame o acima no getView de um adaptador ao recuperar a imagem da 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
fonte