图片缓存策略
从网络上加载一张图,然后把它显示到UI上是个很简单的事情。当图片变多时,处理起来就有些麻烦了,很典型的应用场景,如ListView,GridView或者ViePager等。我们既需要保证用户看到更多的图片,以免屏幕出现大面积的空白,又要保证内存能Hold住。
GC会自动释放一个没有强引用的图片或者View,这本来是个好事情,但为了让用户来回滚动时还能快速加载老图片,通常会使用图片缓存。
下面分别讨论下,通过使用Memory Cache和Disk Cache来增加UI的响应速度和流畅度。
一、使用Memory Cache
Memory Cache花费定量的内存来换取对图片的快速访问。可以使用LruCache来快速实现。过去,我们通常使用SoftReference或WeakReference来实现Memory Cache,但是在Android 2.3 (API Level 9)之后,GC对SoftReference和WeakReference回收的更快,所以,SoftReference和WeakReference的效果就很差了。而且,在Android 3.0 (API Level 11)之前,Bitmap的像素数据存放在Native Memory中,如何释放是不确定的,存在着内存泄漏,甚至Crash的风险。
为了确定LruCache的大小,我们需要参考如下几个因素:
1、当前进程的剩余内存有多少
2、为了保证良好的用户体验,应用场景显示多少张图片可以满足要求?
3、屏幕的density是多少?不同像素的屏幕对图片质量的要求是不一样的,可以据此在取图时做一些优化。
4、图片被访问的频率是否相差很多?如果是这样的话可以存放在不同的LruCache中。
5、有时需要在数量和质量之间做一些平衡。比如为图片列表在Memory Cache中缓存
较多的缩略图,在UI中先显示缩略图,然后在后台加载一张高清的。
这里没有一个精确的数字来做为指导,需要我们在实际中就以上几个因素进行权衡,设置合适的值。至于如何使用LruCache可以参考Support Library中的Demo。
二、使用Disk Cache
Memory Cache可以加快图片的访问速度,但内存有限,不可能所有图片都放在Memory Cache中。当程序先被挤出内存,后又被重新启动时,图片仍需要重新获取。这个时候,可以应用Disk Cache来缓存图片,图片就不需要从网络上获取了,节省了流量,减少了获取时间。但需要注意,Disk Cache的读取涉及到本地IO操作,需要开启后台线程操作。
我们可以像系统Gallery那样使用ContentProvider。另外,Disk Cache也可以很简单地通过Android源码中提供的DiskLruCache来实现。
一、内存缓存
//需要导入外部jar文件 android-support-v4.jar
import
android.support.v4.util.LruCache;
//开辟8M硬缓存空间
private final int hardCachedSize = 8*1024*1024;
//hard cache 强引用
private final LruCache<String, Bitmap> sHardBitmapCache = new LruCache<String, Bitmap>(hardCachedSize)
{
@Override
public int sizeOf(String key, Bitmap value)
{
return value.getRowBytes() *
value.getHeight();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue)
{
Log.v("tag",
"hard cache is full , push to soft cache");
//硬引用缓存区满,将一个最不经常使用的oldvalue推入到软引用缓存区
sSoftBitmapCahe.put(key, new SoftReference<Bitmap>(oldValue));
}
}
//软引用
private static final int SOFT_CACHE_CAPACITY = 40;
private final static LinkedHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new LinkedHashMao<String, SoftReference<Bitmap>>(SOFT_CACHE_CAPACITY, 0.75f, true)
{
@Override
public SoftReference<Bitmap> put(String key, SoftReference<Bitmap> value)
{
return super.input(key, value);
}
@Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<Stirng, SoftReference<Bitmap>> eldest)
{
if(size() > SOFT_CACHE_CAPACITY)
{
Log.v("tag", "Soft Reference limit , purge one");
return true;
}
return false;
}
}
//缓存bitmap
public boolean putBitmap(String key, Bitmap bitmap)
{
if(bitmap != null)
{
synchronized(sHardBitmapCache)
{
sHardBitmapCache.put(key, bitmap);
}
return true;
}
return false;
}
//从缓存中获取bitmap
public Bitmap getBitmap(String key)
{
synchronized(sHardBitmapCache)
{
final Bitmap bitmap = sHardBitmapCache.get(key);
if(bitmap != null)
{
return bitmap;
}
}
//硬引用缓存区间中读取失败,从软引用缓存区间读取
synchronized(sSoftBitmapCache)
{
SoftReference<Bitmap>
bitmapReference = sSoftBtimapCache.get(key);
if(bitmapReference != null)
{
final Bitmap bitmap2 = bitmapReference.get();
if(bitmap2 != null)
{
return bitmap2;
}
else
{
Log.v("tag",
"soft reference 已经被回收");
sSoftBitmapCache.remove(key);
}
}
}
return null;
}
二、磁盘文件缓存
private File mCacheDir = context.getCacheDir();
private static final int MAX_CACHE_SIZE = 20 * 1024 * 1024; //20M
private final LruCache<String, Long> sFileCache = new LruCache<String, Long>(MAX_CACHE_SIZE)
{
@Override
public int sizeOf(String
key, Long value)
{
return value.intValue();
}
@Override
protected void entryRemoved(boolean evicted, String key, Long oldValue, Long newValue)
{
File file = getFile(key);
if(file != null)
{
file.delete();
}
}
private File getFile(String fileName) throws FileNotFoundException
{
File file = new File(mCacheDir, fileName);
if(!file.exists() || !file.isFile())
{
throw new FileNotFoundException("文件不存在或有同名文件夹");
}
return file;
}
//缓存bitmap到外部存储
public boolean putBitmap(String key, Bitmap bitmap)
{
File file = getFile(key);
if(file != null)
{
Log.v("tag", "文件已经存在");
return true;
}
FileOutputStream fos = getOutputStream(key);
boolean saved = bitmap.compress(CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
if(saved)
{
synchronized(sFileCache)
{
sFileCache.put(key, getFile(key).length());
}
return true;
}
return false;
}
//根据key获取OutputStream
private FileOutputStream getOutputStream(String key)
{
if(mCacheDir == null)
return null;
FileOutputStream fos = new FileOutputStream(mCacheDir.getAbsolutePath() + File.separator + key);
return fos;
}
//获取bitmap
private static BitmapFactory.Options sBitmapOptions;
static
{
sBitmapOptions = new BitmapFactory.Options();
sBitmapOptions.inPurgeable=true; //bitmap
can be purged to disk
}
public Bitmap getBitmap(String key)
{
File bitmapFile = getFile(key);
if(bitmapFile != null)
{
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, sBitmapOptions);
if(bitmap != null)
{
//重新将其缓存至硬引用中
...
}
}
}
}
原文:http://www.cnblogs.com/shaweng/p/3920693.html