Respostas:
E agora a dica: use o cache do sistema.
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
Fornece memória e cache flash-rom, compartilhados com o navegador.
grr. Eu gostaria que alguém tivesse me dito isso antes de escrever meu próprio gerenciador de cache.
connection.getContent()
sempre retorna um InputStream para mim, o que estou fazendo de errado?
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Em relação à connection.setUseCaches
solução elegante acima: infelizmente, ela não funcionará sem um esforço adicional. Você precisará instalar um ResponseCache
usando ResponseCache.setDefault
. Caso contrário, HttpURLConnection
silenciosamente ignorará o setUseCaches(true)
bit.
Veja os comentários no topo de FileResponseCache.java
para detalhes:
(Eu publicaria isso em um comentário, mas aparentemente não tenho karma SO suficiente.)
HttpResponseCache
, você pode encontrar o HttpResponseCache.getHitCount()
0. retornado. Não tenho certeza, mas acho que é porque o servidor da web que você está solicitando não usa cabeçalhos de cache nesse caso. Para fazer o cache funcionar de qualquer maneira, use connection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
.
.getContent()
método, porque as respostas 304 não têm um corpo de resposta associado pelo padrão RFC.
Converta-os em bitmaps e, em seguida, armazene-os em uma coleção (HashMap, lista etc.) ou escreva-os no cartão SD.
Ao armazená-los no espaço do aplicativo usando a primeira abordagem, convém agrupá-los em torno de um java.lang.ref.SoftReference especificamente se seus números forem grandes (para que eles sejam coletados durante a crise). Isso poderia resultar em uma recarga.
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
escrevê-los no cartão SD não exigirá uma recarga; apenas uma permissão do usuário.
Uri
referência de caminho para a qual você possa passar ImageView
e outras visualizações personalizadas. Porque cada vez que compress
você perde qualidade. Claro que isso é verdade apenas para algoritmos com perdas. Esse método também permite armazenar um hash do arquivo e usá-lo na próxima vez que você solicitar o arquivo do servidor através de If-None-Match
e ETag
cabeçalhos.
Use LruCache
para armazenar em cache imagens com eficiência. Você pode ler sobre LruCache
no site do desenvolvedor do Android
Eu usei abaixo solução para imagens baixar e cache no android. Você pode seguir as etapas abaixo:
PASSO 1:
torne a classe nomeada ImagesCache
. Eu useiSingleton object for this class
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
public class ImagesCache
{
private LruCache<String, Bitmap> imagesWarehouse;
private static ImagesCache cache;
public static ImagesCache getInstance()
{
if(cache == null)
{
cache = new ImagesCache();
}
return cache;
}
public void initializeCache()
{
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);
final int cacheSize = maxMemory / 8;
System.out.println("cache size = "+cacheSize);
imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
{
protected int sizeOf(String key, Bitmap value)
{
// The cache size will be measured in kilobytes rather than number of items.
int bitmapByteCount = value.getRowBytes() * value.getHeight();
return bitmapByteCount / 1024;
}
};
}
public void addImageToWarehouse(String key, Bitmap value)
{
if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
{
imagesWarehouse.put(key, value);
}
}
public Bitmap getImageFromWarehouse(String key)
{
if(key != null)
{
return imagesWarehouse.get(key);
}
else
{
return null;
}
}
public void removeImageFromWarehouse(String key)
{
imagesWarehouse.remove(key);
}
public void clearCache()
{
if(imagesWarehouse != null)
{
imagesWarehouse.evictAll();
}
}
}
PASSO 2:
faça outra classe chamada DownloadImageTask que é usada se o bitmap não estiver disponível no cache, ele fará o download aqui:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{
private int inSampleSize = 0;
private String imageUrl;
private BaseAdapter adapter;
private ImagesCache cache;
private int desiredWidth, desiredHeight;
private Bitmap image = null;
private ImageView ivImageView;
public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight)
{
this.adapter = adapter;
this.cache = ImagesCache.getInstance();
this.desiredWidth = desiredWidth;
this.desiredHeight = desiredHeight;
}
public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
{
this.cache = cache;
this.ivImageView = ivImageView;
this.desiredHeight = desireHeight;
this.desiredWidth = desireWidth;
}
@Override
protected Bitmap doInBackground(String... params)
{
imageUrl = params[0];
return getImage(imageUrl);
}
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
if(result != null)
{
cache.addImageToWarehouse(imageUrl, result);
if(ivImageView != null)
{
ivImageView.setImageBitmap(result);
}
else if(adapter != null)
{
adapter.notifyDataSetChanged();
}
}
}
private Bitmap getImage(String imageUrl)
{
if(cache.getImageFromWarehouse(imageUrl) == null)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = inSampleSize;
try
{
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
int imageWidth = options.outWidth;
int imageHeight = options.outHeight;
if(imageWidth > desiredWidth || imageHeight > desiredHeight)
{
System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);
inSampleSize = inSampleSize + 2;
getImage(imageUrl);
}
else
{
options.inJustDecodeBounds = false;
connection = (HttpURLConnection)url.openConnection();
stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
return image;
}
}
catch(Exception e)
{
Log.e("getImage", e.toString());
}
}
return image;
}
PASSO 3: Uso do seu Activity
ouAdapter
Nota: Se você deseja carregar a imagem do URL da Activity
Classe. Use o segundo Construtor de DownloadImageTask
, mas se você deseja exibir uma imagem, Adapter
use o primeiro Construtor de DownloadImageTask
(por exemplo, você tem uma imagem ListView
e está definindo a imagem em 'Adaptador')
USO DA ATIVIDADE:
ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.
imgTask.execute(img);
}
USO DO ADAPTADOR:
ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.
imgTask.execute(img);
}
Nota:
cache.initializeCache()
você pode usar esta declaração na primeira atividade do seu aplicativo. Depois de inicializar o cache, você nunca precisará inicializá-lo todas as vezes se estiver usando uma ImagesCache
instância.
Eu nunca sou bom em explicar as coisas, mas espero que isso ajude os iniciantes a como usar o cache LruCache
e seu uso :)
EDITAR:
Hoje em dia, existem bibliotecas muito famosas conhecidas como Picasso
e Glide
que podem ser usadas para carregar imagens com muita eficiência no aplicativo Android. Experimente esta biblioteca Picasso muito simples e útil para Android e Glide For Android . Você não precisa se preocupar com imagens de cache.
O Picasso permite o carregamento de imagens sem problemas no seu aplicativo - geralmente em uma linha de código!
O Glide, assim como o Picasso, pode carregar e exibir imagens de várias fontes, além de cuidar do cache e manter um baixo impacto na memória ao manipular imagens. Ele foi usado por aplicativos oficiais do Google (como o Google I / O 2015) e é tão popular quanto o Picasso. Nesta série, exploraremos as diferenças e vantagens do Glide sobre Picasso.
Você também pode visitar o blog para saber a diferença entre Glide e Picasso
if(cache == null)
que resolveu meu problema! :)
Para baixar uma imagem e salvar no cartão de memória, você pode fazer o seguinte.
//First create a new URL object
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")
//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");
//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));
Não se esqueça de adicionar a permissão da Internet ao seu manifesto:
<uses-permission android:name="android.permission.INTERNET" />
Eu consideraria usar o cache de imagem do droidfu. Ele implementa um cache de imagem na memória e baseado em disco. Você também recebe um WebImageView que tira proveito da biblioteca ImageCache.
Aqui está a descrição completa do droidfu e do WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
Eu tentei o SoftReferences, eles são recuperados de forma muito agressiva no Android e eu senti que não havia sentido em usá-los
SoftReference
s. Eles recomendam o uso deles LruCache
.
Como o Thunder Rabbit sugeriu, o ImageDownloader é o melhor para o trabalho. Também encontrei uma pequena variação da classe em:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
A principal diferença entre os dois é que o ImageDownloader usa o sistema de cache do Android e o modificado usa armazenamento interno e externo como cache, mantendo as imagens em cache indefinidamente ou até que o usuário as remova manualmente. O autor também menciona a compatibilidade com o Android 2.1.
Essa é uma boa notícia para Joe. O exemplo de código acima tem dois problemas - um - o objeto de resposta não é uma instância do Bitmap (quando meu URL faz referência a um jpg, como http: \ website.com \ image.jpg, é um
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).
Segundo, como Joe ressalta, nenhum cache ocorre sem que um cache de resposta esteja configurado. Os desenvolvedores do Android têm que rolar seu próprio cache. Aqui está um exemplo para fazer isso, mas ele apenas armazena em cache a memória, o que realmente não é a solução completa.
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
A API de armazenamento em cache do URLConnection é descrita aqui:
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
Ainda acho que essa é uma solução boa para seguir esse caminho - mas você ainda precisa escrever um cache. Parece divertido, mas eu prefiro escrever recursos.
Há uma entrada especial na seção de treinamento oficial do Android sobre isso: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
A seção é bastante nova, não estava lá quando a pergunta foi feita.
A solução sugerida é usar um LruCache. Essa classe foi introduzida no Honeycomb, mas também está incluída na biblioteca de compatibilidade.
Você pode inicializar um LruCache configurando o número máximo ou entradas e ele automaticamente os classifica como você e os limpa menos usados quando você ultrapassa o limite. Fora isso, ele é usado como um mapa normal.
O código de amostra da página oficial:
private LruCache mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
Anteriormente, as SoftReferences eram uma boa alternativa, mas não mais, citando a partir da página oficial:
Nota: No passado, uma implementação popular de cache de memória era um cache de bitmap SoftReference ou WeakReference, no entanto, isso não é recomendado. A partir do Android 2.3 (API Nível 9), o coletor de lixo é mais agressivo ao coletar referências suaves / fracas, o que as torna bastante ineficazes. Além disso, antes do Android 3.0 (API Nível 11), os dados de backup de um bitmap eram armazenados na memória nativa, que não é liberada de maneira previsível, potencialmente fazendo com que um aplicativo excedesse brevemente seus limites de memória e falhas.
Considere usar a biblioteca Universal Image Loader de Sergey Tarasevich . Vem com:
O Universal Image Loader permite gerenciamento detalhado de cache para imagens baixadas, com as seguintes configurações de cache:
UsingFreqLimitedMemoryCache
: O bitmap usado com menos frequência é excluído quando o limite de tamanho do cache é excedido.LRULimitedMemoryCache
: O bitmap usado menos recentemente é excluído quando o limite de tamanho do cache é excedido.FIFOLimitedMemoryCache
: A regra FIFO é usada para exclusão quando o limite de tamanho do cache é excedido.LargestLimitedMemoryCache
: O maior bitmap é excluído quando o limite de tamanho do cache é excedido.LimitedAgeMemoryCache
: O objeto em cache é excluído quando sua idade excede o valor definido .WeakMemoryCache
: Um cache de memória com apenas referências fracas a bitmaps.Um exemplo simples de uso:
ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png";
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);
Este exemplo usa o padrão UsingFreqLimitedMemoryCache
.
O que realmente funcionou para mim foi definir o ResponseCache na minha classe Principal:
try {
File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { }
e
connection.setUseCaches(true);
ao baixar bitmap.
http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html
O libs-for-android do Google possui ótimas bibliotecas para gerenciar o cache de imagens e arquivos.
Eu estava lutando com isso há algum tempo; as respostas usando o SoftReferences perderiam seus dados muito rapidamente. As respostas que sugerem instanciar um RequestCache eram muito complicadas, e eu nunca consegui encontrar um exemplo completo.
Mas ImageDownloader.java funciona maravilhosamente para mim. Ele usa um HashMap até que a capacidade seja atingida ou até que o tempo limite da limpeza ocorra, depois as coisas são movidas para um SoftReference, usando assim o melhor dos dois mundos.
Eu sugiro que a IGNIÇÃO seja ainda melhor do que o Droid fu
https://github.com/kaeppler/ignition
https://github.com/kaeppler/ignition/wiki/Sample-applications
Resposta ainda mais tarde, mas escrevi um Android Image Manager que lida com o cache de forma transparente (memória e disco). O código está no Github https://github.com/felipecsl/Android-ImageManager
Resposta tardia, mas achei que deveria adicionar um link ao meu site porque escrevi um tutorial sobre como fazer um cache de imagens para o Android: http://squarewolf.nl/2010/11/android-image-cache/ Update: the A página foi colocada offline porque a fonte estava desatualizada. Associo-me à @ elenasys nos conselhos dela para usar o Ignition .
Portanto, para todas as pessoas que se deparam com essa pergunta e não encontraram uma solução: espero que gostem! = D
Resposta tardia, mas acho que esta biblioteca ajudará muito no armazenamento de imagens em cache: https://github.com/crypticminds/ColdStorage .
Basta anotar o ImageView com @LoadCache (R.id.id_of_my_image_view, "URL_to_downlaod_image_from) e ele cuidará do download da imagem e do carregamento na visualização da imagem. Também é possível especificar uma imagem de espaço reservado e carregar a animação.
A documentação detalhada da anotação está presente aqui: - https://github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation