首页 > 代码库 > 图片缓存策略
图片缓存策略
图片缓存策略
1、图片缓存策略分析
从网络上加载一张图,然后把它显示到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来实现。
2、图片缓存实现
一、内存缓存
//需要导入外部jar文件 android-support-v4.jar
importandroid.support.v4.util.LruCache;
//开辟8M硬缓存空间
private final int hardCachedSize =8*1024*1024;
//hardcache 强引用
private final LruCache<String,Bitmap> sHardBitmapCache = new LruCache<String, Bitmap>(hardCachedSize)
{
@Override
public int sizeOf(Stringkey, Bitmap value)
{
return value.getRowBytes() *value.getHeight();
}
@Override
protected voidentryRemoved(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 intSOFT_CACHE_CAPACITY = 40;
private final static LinkedHashMap<String,SoftReference<Bitmap>> sSoftBitmapCache = new LinkedHashMao<String,SoftReference<Bitmap>>(SOFT_CACHE_CAPACITY, 0.75f, true)
{
@Override
publicSoftReference<Bitmap> put(String key, SoftReference<Bitmap> value)
{
returnsuper.input(key, value);
}
@Override
protectedboolean 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 Bitmapbitmap = sHardBitmapCache.get(key);
if(bitmap !=null)
{
return bitmap;
}
}
//硬引用缓存区间中读取失败,从软引用缓存区间读取
synchronized(sSoftBitmapCache)
{
SoftReference<Bitmap>bitmapReference = sSoftBtimapCache.get(key);
if(bitmapReference != null)
{
final Bitmapbitmap2 = 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(Stringkey, Long value)
{
return value.intValue();
}
@Override
protected void entryRemoved(booleanevicted, 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())
{
thrownew FileNotFoundException("文件不存在或有同名文件夹");
}
return file;
}
//缓存bitmap到外部存储
public boolean putBitmap(String key, Bitmapbitmap)
{
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 FileOutputStreamgetOutputStream(String key)
{
if(mCacheDir == null)
return null;
FileOutputStream fos = newFileOutputStream(mCacheDir.getAbsolutePath() + File.separator + key);
return fos;
}
//获取bitmap
private static BitmapFactory.OptionssBitmapOptions;
static
{
sBitmapOptions = newBitmapFactory.Options();
sBitmapOptions.inPurgeable=true; //bitmapcan 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)
{
//重新将其缓存至硬引用中
...
}
}
}
}
摘自:
android读取大图片并缓存 http://www.cnblogs.com/leehongee/p/3323837.html
图片缓存实现策略分析 http://blog.csdn.net/a345017062/article/details/8753649