LruCache, 虽然很多文章都把 LRU 翻译成 "最近最少使用" 缓存策略, 但 Android 中的 LruCache 真的如此吗?
答案是 No, 它只做到了控制 "最近使用"!
原理
数据结构
LruCache 采用 LinkedHashMap 作为存储的数据结构, 那么为什么是 LinkedHashMap?
LinkedHashMap 特性简介
LinkedHashMap 基于 HashMap, 具有 HashMap 高效查找, 自动扩容等特性
在 HashMap 基础上, 增加了一个双向链表存储 K-V 对, 实现了自己的遍历器 LinkedHashIterator, 默认情况下可以做到根据数据插入顺序有序地遍历
提供重载构造方法供外部控制 accessOrder, 以实现根据访问顺序有序地遍历
初始化
- public LruCache(int maxSize) {
- if (maxSize <= 0) {
- throw new IllegalArgumentException("maxSize <= 0");
- }
- this.maxSize = maxSize;
- this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
- }
LruCache 的构造方法如上, 可见 LruCache 初始化时就使用了上面提到的 LinkedHashMap 的第三点特性, 即在数据结构层面实现了 "最近使用".
存储
当调用 put 方法添加 / 设置存储内容时, LruCache 依次做了这么几件事:
判空, 即不允许 key/value 为 null
总容量 size 增加上计算传入的 K-V 大小
将传入的 K-V 存入 LinkedHashMap
如过 LinkedHashMap 中已存在相同 K, 总容量 size 减去替换掉的 K-VOld 大小
通知 VOld 被替换(子类实现 entryRemoved 以监听)
比较总容量 size 和最大容量 maxSize, 若大于 maxSize 则循环移除 LinkedHashMap 头结点 (即最久未被访问的结点) 直至 size 小于等于 maxSize
获取
当调用 get 方法获取存储内容时, LruCache 依次做了这些事:
判空
从 LinkedHashMap 中取出与 K 对应的 V 值并返回. 如果子类未实现 create 方法以达到当缓存未命中时创建并存入新 V 的话, 返回 null,get 流程结束.
通过 create 创建 VNew, 并尝试把 VNew 存储到 LinkedHashMap 中, 流程类似存储过程, 不同之处在于当 K 冲突时, 会舍弃掉新创建的 VNew. 不要奇怪为什么明明上面通过 K 取 V 的时候没取到, 这里却会 K 冲突, 因为 LruCache 为了性能考虑(防止子类自定义的 create 方法耗时过长影响 get 方法执行性能), 只对从 LinkedHashMap 中取值的过程做了同步处理, 这样在多线程的情况下就可能出现 A 线程在 create 的时候, B 线程已经将 K-VB 存入了 map.
返回上面创建的 VNew 或者 VB
使用
用 LruCache 实现一个简单的图片缓存
- class LruImageCache extends LruCache<String, Bitmap> {
- private static final String TAG = "LruImageCache";
- private static final int DEFAULT_CACHE_SIZE = 20 * 1024 * 1024;
- public LruImageCache() {
- this(DEFAULT_CACHE_SIZE);
- }
- public LruImageCache(int maxSize) {
- super(maxSize);
- }
- @Override
- protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
- Log.d(TAG, "cache removed:" + key);
- }
- @Override
- protected Bitmap create(String key) {
- // 从本地, 网络获取图片
- return loadImageFromIO(key);
- }
- @Override
- protected int sizeOf(String key, Bitmap value) {
- return value.getAllocationByteCount();
- }
- }
- // 使用
- Bitmap b = LruImageCache.get("http://image-path");
来源: https://www.cnblogs.com/duanzi6/p/13409996.html