2/2:Disk Cache(磁盘中的Cache)
前面已经提到,Memory Cache的优点是读写非常快。但它的缺点就是容量太小了,而且不能持久化,所以在用户在滑动GridView时它很快会被用完,而且切换多个界面时或者是关闭程序重新打开后,再次进入原来的界面,Memory Cache是无能为力的。这个时候,我们就要用到Disk Cache了。
Disk Cache将缓存的数据放在磁盘中,因此不论用户是频繁切换界面,还是关闭程序,Disk Cache是不会消失的。
实际上,Android SDK中并没有一个类来实现Disk Cache这样的功能。但google其实已经提供了实现代码:DiskLruCache。我们只要把它搬到自己的项目中就可以了。
下面请看一段使用DiskLruCache来配合Memory Cache进行图片缓存的代码:
private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
// 初始化memory cache
//...
// 开启后台线程初始化disk cache
File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
new InitDiskCacheTask().execute(cacheDir);
}
class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
@Override
protected Void doInBackground(File... params) {
synchronized (mDiskCacheLock) {
File cacheDir = params[0];
mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
mDiskCacheStarting = false; // 初始化完成
mDiskCacheLock.notifyAll(); // 唤醒被hold住的线程
}
return null;
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
// 在后台加载图片
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]);
// 通过后台线程检查disk cache
Bitmap bitmap = getBitmapFromDiskCache(imageKey);
if (bitmap == null) { // 如果没有在disk cache中发现这个bitmap
// 加载这个bitmap
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
}
// 把这个bitmap加入cache
addBitmapToCache(imageKey, bitmap);
return bitmap;
}
}
public void addBitmapToCache(String key, Bitmap bitmap) {
// 把bitmap加入memory cache
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
// 同样,也加入disk cache
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
mDiskLruCache.put(key, bitmap);
}
}
}
public Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
// 等待disk cache初始化完毕
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {}
}
if (mDiskLruCache != null) {
return mDiskLruCache.get(key);
}
}
return null;
}
// 在自带的cache目录下建立一个独立的子目录。优先使用外置存储。但如果外置存储不存在,使用内置存储。
public static File getDiskCacheDir(Context context, String uniqueName) {
// 如果MEDIA目录已经挂载或者外置存储是手机自带的(Nexus设备都这么干),使用外置存储;否则使用内置存储
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
提示:由于disk cache的初始化是耗时操作,所以这个过程被放在了后台进程。而由此导致的结果是,主线程有可能在它初始化完成之前就尝试读取disk cache,这会导致程序出错。因此以上代码中使用了synchronized关键字和一个lock对象来确保在初始化完成之前disk cache不会被访问。(什么是synchronized?文章最后会有介绍)
上面这段代码看起来比较多,但大致读一下就会发现,它的思路非常简单:1.读取cache的时候,优先读取memory cache,读不到的时候再读取disk cache;2.把bitmap保存到cache中的时候,memory cache和disk cache都要保存。
1627

被折叠的 条评论
为什么被折叠?



