最近结合郭神的博客学习了下图像加载的三级缓存-网络层、硬盘层、内存层。
利用listView、Gridview、Viwepage等加载图片时,首先看下内存里是否有该图片(因为内存加载图片较于硬盘更快),没有的话没再去看硬盘有没有,再没有的话就去只能使用网络下载啦,下载后的图片再保存到硬盘以及本地内存中以待下次使用。
内存层
内存缓存之前用的是软引用SoftReference,但是(1)因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。
(2)另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。
现在推荐的内存缓存用到的是DiskLruCache类(Lru - Least Recently Used最近最久未使用算法,底层原理https://blog.csdn.net/u013795543/article/details/89294198),它会把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除,从而避免多图OOM。
初始化DiskLruCache分配内存空间大小:
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
Log.d(TAG, "PhotoWallAdapter,sizeOf,bitmap.getByteCount()=" + bitmap.getByteCount());
return bitmap.getByteCount();
}
};
添加图片到内存:
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
从内存获取图片:
private Bitmap getBitmapFromMemoryCache(String key) {
return mMemoryCache.get(key);
}
硬盘层
DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,可以指定图片的缓存位置(比如/sdcard/Android/data//cache )和大小(貌似没有限制)。
DiskLruCache并不在gOOGLE官方api中,其源码在Google Source上下载,地址如下:
android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java
初始化:
try {
File cacheDir = getDiskCacheDir(context, "thumb");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {
e.printStackTrace();
}
DiskLruCache是不能new出实例的,如果我们要创建一个DiskLruCache的实例,则需要调用它的open()方法,open()方法接收四个参数,第一个参数指定的是数据的缓存地址,第二个参数指定当前应用程序的版本号,第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1,第四个参数指定最多可以缓存多少字节的数据。
写入硬盘缓存借助DiskLruCache.Editor这个类完成的,这个类也是不能new的,需要调用DiskLruCache的edit()方法来获取实例如下:先去获取,如果获取不到则从网络获取并保存到硬盘缓存
snapshot = mDiskLruCache.get(key);
if (snapshot == null) {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
if (downloadUrlToStream(imageUrl, outputStream)) {
editor.commit();
}else {
editor.abort();
}
}
snapshot=mDiskLruCache.get(key);
}
网络层
就是利用网络下载图片,不详谈。
在Activity的onPause()方法中去调用一次DiskLruCache的flush()方法,该方法用于将内存中的操作记录同步到日志文件(也就是journal文件)当中。在Activity的onDestroy()方法中去调用DiskLruCache的close()方法,该方法是和open()方法对应的一个方法。关闭掉了之后就不能再调用DiskLruCache中任何操作缓存数据的方法。