Android 安卓图片缓存
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1945201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android image caching
提问by d-man
How can I cache images after they are downloaded from web?
从网络下载图像后如何缓存图像?
回答by edrowland
And now the punchline: use the system cache.
现在的重点是:使用系统缓存。
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
Provides both memory and flash-rom cache, shared with the browser.
提供内存和闪存缓存,与浏览器共享。
grr. I wish somebody had told ME that before i wrote my own cache manager.
老大 我希望有人在我编写自己的缓存管理器之前告诉我。
回答by Joe
Regarding the elegant connection.setUseCaches
solution above: sadly, it won't work without some additional effort. You will need to install a ResponseCache
using ResponseCache.setDefault
. Otherwise, HttpURLConnection
will silently ignore the setUseCaches(true)
bit.
关于connection.setUseCaches
上面的优雅解决方案:遗憾的是,如果不做一些额外的努力,它将无法工作。您将需要安装一个ResponseCache
using ResponseCache.setDefault
. 否则,HttpURLConnection
将默默地忽略该setUseCaches(true)
位。
See the comments at the top of FileResponseCache.java
for details:
详情请参阅顶部的评论FileResponseCache.java
:
(I'd post this in a comment, but I apparently don't have enough SO karma.)
(我会在评论中发表这个,但我显然没有足够的业力。)
回答by Samuh
Convert them into Bitmaps and then either store them in a Collection(HashMap,List etc.) or you can write them on the SDcard.
将它们转换为位图,然后将它们存储在集合(HashMap、List 等)中,或者您可以将它们写入 SD 卡。
When storing them in application space using the first approach, you might want to wrap them around a java.lang.ref.SoftReferencespecifically if their numbers is large (so that they are garbage collected during crisis). This could ensue a Reload though.
当使用第一种方法将它们存储在应用程序空间中时,您可能希望将它们包装在java.lang.ref.SoftReference 中,特别是如果它们的数量很大(以便在危机期间将它们收集起来)。不过,这可能会导致重新加载。
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
writing them on SDcard will not require a Reload; just a user-permission.
将它们写入 SD 卡不需要重新加载;只是一个用户权限。
回答by Zubair Ahmed
Use LruCache
to cache images efficiently. You can read about LruCache
from Android Developer site
用于LruCache
高效缓存图像。您可以LruCache
从Android 开发者网站阅读
I've used below solution for Images download and caching in android. You can follow steps below:
我已经使用以下解决方案在 android 中下载和缓存图像。您可以按照以下步骤操作:
STEP 1:make Class Named ImagesCache
. I've used Singleton object for this class
第 1 步:使 Class Named ImagesCache
。我用过Singleton 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();
}
}
}
STEP 2:
第2步:
make another class named DownloadImageTask which is used if bitmap is not available in cache it will download it from here:
创建另一个名为 DownloadImageTask 的类,如果位图在缓存中不可用,它将从这里下载它:
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;
}
STEP 3:Usage from your Activity
or Adapter
第 3 步:从您的Activity
或Adapter
Note:If you want to load image from url from Activity
Class. Use the second Constructor of DownloadImageTask
, but if you want to display image from Adapter
use first Constructor of DownloadImageTask
(for example you have a image in ListView
and you are setting image from 'Adapter')
注意:如果您想从Activity
Class 中的url 加载图像。使用的第二个构造函数DownloadImageTask
,但是如果你想从Adapter
使用第一个构造函数的显示图像DownloadImageTask
(例如你有一个图像ListView
并且你正在从“适配器”设置图像)
USAGE FROM ACTIVITY:
活动用途:
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);
}
USAGE FROM ADAPTER:
来自适配器的用法:
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);
}
Note:
笔记:
cache.initializeCache()
you can use this statement in the very first Activity of your application. Once you've initialized the cache you would never need to initialized it every time if you are using ImagesCache
instance.
cache.initializeCache()
您可以在应用程序的第一个 Activity 中使用此语句。一旦你初始化了缓存,如果你使用ImagesCache
实例,你就不需要每次都初始化它。
I am never good at explaining things but hope this will help the beginners that how to cache using LruCache
and its usage :)
我从来不擅长解释事情,但希望这能帮助初学者了解如何缓存使用LruCache
及其用法:)
EDIT:
编辑:
Now a days there are very famous libraries known as Picasso
and Glide
which can be used to load images very efficiently in android app. Try this very simple and usefull library Picasso for androidand Glide For Android. You do not need to worry about cache images.
现在有一些非常著名的库Picasso
,Glide
它们可以用来在 android 应用程序中非常有效地加载图像。试试这个非常简单和有用的库Picasso for android和Glide For Android。您无需担心缓存图像。
Picasso allows for hassle-free image loading in your application—often in one line of code!
Glide, just like Picasso, can load and display images from many sources, while also taking care of caching and keeping a low memory impact when doing image manipulations. It has been used by official Google apps (like the app for Google I/O 2015) and is just as popular as Picasso. In this series, we're going to explore the differences and advantages of Glide over Picasso.
Picasso 允许在您的应用程序中轻松加载图像——通常只需一行代码!
Glide 和 Picasso 一样,可以从许多来源加载和显示图像,同时在进行图像处理时还负责缓存和保持较低的内存影响。它已被 Google 官方应用程序(如 Google I/O 2015 的应用程序)使用,并且与 Picasso 一样受欢迎。在本系列中,我们将探讨 Glide 相对于 Picasso 的差异和优势。
You can also visit blog for difference between Glide and Picasso
您也可以访问博客了解Glide 和 Picasso 之间的区别
回答by Ljdawson
To download an image and save to the memory card you can do it like this.
要下载图像并保存到存储卡,您可以这样做。
//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));
Don't forget to add the Internet permission to your manifest:
不要忘记在清单中添加 Internet 权限:
<uses-permission android:name="android.permission.INTERNET" />
回答by esilver
I would consider using droidfu's image cache. It implements both an in-memory and disk-based image cache. You also get a WebImageView that takes advantage of the ImageCache library.
我会考虑使用 droidfu 的图像缓存。它实现了内存中和基于磁盘的图像缓存。您还将获得一个利用 ImageCache 库的 WebImageView。
Here is the full description of droidfu and WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
以下是 droidfu 和 WebImageView 的完整描述:http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
回答by 2cupsOfTech
I've tried SoftReferences, they are too aggressively reclaimed in android that I felt there was no point using them
我已经尝试过 SoftReferences,它们在 android 中过于激进地回收,我觉得使用它们没有意义
回答by EZFrag
As Thunder Rabbit suggested, ImageDownloader is the best one for the job. I also found a slight variation of the class at:
正如 Thunder Rabbit 所建议的那样,ImageDownloader 是这项工作的最佳选择。我还在以下位置发现了该课程的细微变化:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
http://theandroidcoder.com/utilities/android-image-download-and-caching/
The main difference between the two is that the ImageDownloader uses the Android caching system, and the modified one uses internal and external storage as caching, keeping the cached images indefinitely or until the user removes it manually. The author also mentions Android 2.1 compatibility.
两者的主要区别在于ImageDownloader使用的是Android缓存系统,修改后的使用内部和外部存储作为缓存,无限期地保留缓存的图像或直到用户手动删除它。作者还提到了Android 2.1兼容性。
回答by shalafi
There is a special entry on the official training section of Android about this: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Android的官方培训部分有一个关于此的特殊条目:http: //developer.android.com/training/displaying-bitmaps/cache-bitmap.html
The section is quite new, it was not there when the question was asked.
该部分很新,当提出问题时它不在那里。
The suggested solution is to use a LruCache. That class was introduced on Honeycomb, but it is also included on the compatibility library.
建议的解决方案是使用 LruCache。该类是在 Honeycomb 上引入的,但它也包含在兼容性库中。
You can initialize a LruCache by setting the maximum number or entries and it will automatically sort them your you and clean them less used ones when you go over the limit. Other than that it is used as a normal Map.
您可以通过设置最大数量或条目来初始化 LruCache,当您超过限制时,它会自动对您进行排序并清理较少使用的。除此之外,它用作法线贴图。
The sample code from the official page:
来自官方页面的示例代码:
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);
}
Previously SoftReferences were a good alternative, but not anymore, quoting from the official page:
以前 SoftReferences 是一个不错的选择,但现在不再是了,引自官方页面:
Note: In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
注意:过去,流行的内存缓存实现是 SoftReference 或 WeakReference 位图缓存,但不推荐这样做。从 Android 2.3(API 级别 9)开始,垃圾收集器在收集软/弱引用方面更加积极,这使得它们相当低效。此外,在 Android 3.0(API 级别 11)之前,位图的后备数据存储在本机内存中,不会以可预测的方式释放,这可能会导致应用程序短暂超出其内存限制并崩溃。
回答by Peter Pascale
This is a good catch by Joe. The code example above has two problems - one - the response object isn't an instance of Bitmap (when my URL references a jpg, like http:\website.com\image.jpg, its a
这是乔的好球。上面的代码示例有两个问题 - 一个 - 响应对象不是 Bitmap 的实例(当我的 URL 引用 jpg,如 http:\website.com\image.jpg,它是一个
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl$LimitedInputStream).
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl$LimitedInputStream)。
Second, as Joe points out, no caching occurs without a response cache being configured. Android developers are left to roll their own cache. Here's an example for doing so, but it only caches in memory, which really isn't the full solution.
其次,正如 Joe 指出的那样,如果没有配置响应缓存,就不会发生缓存。Android 开发人员只能滚动自己的缓存。这是一个这样做的示例,但它仅缓存在内存中,这确实不是完整的解决方案。
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
The URLConnection caching API is described here:
此处描述了 URLConnection 缓存 API:
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
I still think this is an OK solution to go this route - but you still have to write a cache. Sounds like fun, but I'd rather write features.
我仍然认为这是走这条路的一个不错的解决方案 - 但你仍然需要写一个缓存。听起来很有趣,但我更喜欢写功能。