LruCache和DiskLruCache
前言
Android中的三级缓存主要就是内存缓存和硬盘缓存。
Lru(least recently used)意为最近最少使用算法,核心思想就是当缓存满时,会优先淘汰最近最少使用的缓存对象。
LruCache的使用
在Android中可以直接使用LruCache,算法原理是:把最近使用的对象存储在LinkedHashMap中,当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法类完成缓存的获取和添加操作。
int maxMemory = (int) (Runtime.getRuntime().totalMemory() / 1024);
// 缓存的大小,一般为当前进程可用容量的1/8
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {// 重写sizeOf方法, 计算出要缓存的每张图片的大小@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight() / 1024;}
};
利用LinkedHashMap的一个特性(accessOrder = true基于访问顺序)再加上对LinkedHashMap的数据操作上锁实现的缓存策略
- 首先设置内部的LinkedHashMap构造参数accessOrder = true,实现了数据排序按照访问顺序
- LruCache在调用get()方法是时,会调用LinkedHashMap的get()方法,会将此数据移到队尾
- 最新访问的数据在尾部,如果要在存入数据,将移除队首最近最少访问的数据
再次总结一下原理:
- LruCache中维护了一个LinkedHashMap,该LinkedHashMap是以访问顺序排序的
- 当调用put()方法时,在结合处添加元素,并调用trimToSize()判断缓存是否已满,如果满了删除LinkedHashMap队首的元素
- 当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队尾
具体分析不说了,贴一个我觉得写的好的链接:浅析LRUCache原理(Android)
DiskLruCache
// DiskLruCache是不能new出来的, 需要调用open()方法// 缓存地址, app版本号, 一个Key可对应多少个文件, 最多可以缓存多少数据
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)// SD卡存在
getExternalCahceDir()来获取到的是/sdcard/Android/data/<application package>/cache
// SD卡不存在
getCacheDir()获取到的是/data/data/<application package>/cache// 标准open()写法
DiskLruCache mDiskLruCache = null;
try {File cacheDir = getDiskCacheDir(context, "bitmap);if (!cacheDir.exists()) {cacheDir.mkdirs();}mDiskLruCache = DiskLruCache.open(cacheDir, getApplication(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {e.printStackTrace();
}// 子线程中写入操作, 通过DiskLruCache.Editor
try {String imageUrl = "https://imgmy.csdn.net/uploads/201309/01/1378037235_7476.jpg";String key = hashKeyForDisk(imageUrl);DiskLruCache.Editor editor = mDiskLruCache.edit(key);if (editor != null) {OutputStream outputStream = editor.newOutputStream(0);if (downloadUrlToStream(imageUrl, outputStream)) {editor.commit();} else {editor.abort();}}mDiskLruCache.flush();
} catch (IOException e) {e.printStackTrace();
}// 读取操作, 通过DiskLruCAche.Snapshot
try {String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";String key = hashKeyForDisk(imageUrl);DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);if (snapShot != null) {InputStream is = snapShot.getInputStream(0);Bitmap bitmap = BitmapFactory.decodeStream(is);mImage.setImageBitmap(bitmap);}
} catch (IOException e) {e.printStackTrace();
}// 移除操作, 一般需要从网络重新获取最新数据的时候才应该调用
try {String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg"; String key = hashKeyForDisk(imageUrl); mDiskLruCache.remove(key);
} catch (IOException e) {e.printStackTrace();
}// 其他API// 显示缓存数据大小size()// 将内存中的操作记录同步到日志文件中,不需要每次都调用flush()// 关闭,和open()对应的close()// 将所有的缓存数据清除delete()
解读journal
// 上面三个1依次是:DiskLruCache版本号,应用程序版本号,valueCountdirty: 调用edit()方法时,会向journal文件中写入一条dirty记录, 表示正准备写入,但不知道是什么结果 clean: commit()方法表示写入缓存成功, 会向journal写入一条clean记录, 代表这条脏数据被“清洗”干净了 remove:abort()方法表示写入缓存失败, 会向journal写入一条remove记录 每一行dirty的key, 后面都应该有一行对应的clean和remove的记录, 否则这条数据就是脏的, 会被自动清理掉clean后面还会带有 此文件的字节数除了这些还有read记录, 每当我们调用get()方法获取缓存时, 都会想journal文件中写入一条read记录DiskLruCache使用了一个redundantOpCount变量来记录用户操作的次数, 当变量值达到2000就会重构journal的事件, 保证journal文件的大小适中保持在一个合理的范围内
参考郭神的:Android DiskLruCache完全解析,硬盘缓存的最佳方案
当然还有一篇LruCache和DiskLruCache结合使用的实例:Android照片墙完整版,完美结合LruCache和DiskLruCache
总结
核心内容还是LruCache算法,通过利用LinkedHashMap,实现它最近最少使用的算法,访问过的元素加到队尾,缓存满了就去删除队首的元素,至于DiskLruCache,基本的用法是很简单的,不需要死记硬背。目前就总结这么多,有什么新的东西再总结吧。