-
LRU原理:
- 简写:Least Recently Used
- 即最近最少使用,是一种调度算法或者说淘汰机制。就是每个Item都有一个访问字段t,记录自上次被访问的时间,当需要移除时选择移除t值最大的Item。
-
androidx.collection.LruCache实现
- 基层依赖LinkedHashMap。而LinkedHashMap是一个双向链表。
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); } //accessOrder为false,表示map元素顺序跟随插入顺序,默认值。 //accessOrder为true,表示map元素顺序跟随访问顺序变化。即一个元素被访问时,同时调整其位置将其放到最后,刚好符合lru原则。
- put方法
public final V put(@NonNull K key, @NonNull V value) { V previous; synchronized (this) {//线程安全的 size += safeSizeOf(key, value); //在key位置上放新值value,返回key位置之前的旧值 previous = map.put(key, value); if (previous != null) {//减去旧值的size size -= safeSizeOf(key, previous); } } //如果旧值不为空,则回调entryRemoved方法 if (previous != null) { entryRemoved(false, key, previous, value); } trimToSize(maxSize);//size变化了,检查要不要剔除最老元素 return previous; }
- get方法
public final V get(@NonNull K key) { V mapValue; synchronized (this) { //注意:get方法不仅取值,如果取到了还会更新该元素位置 //即移到链表最后位置tail,因为之前参数accessOrder为true mapValue = map.get(key); if (mapValue != null) { return mapValue;//找到了直接返回 } } V createdValue = create(key);//找不到就创建新的 if (createdValue == null) { return null; } synchronized (this) { mapValue = map.put(key, createdValue); if (mapValue != null) {//基本为null,除非并发 map.put(key, mapValue); } else {//size变化,后面调用trimToSize重新检查调整 size += safeSizeOf(key, createdValue); } } if (mapValue != null) {//并发导致刚好已经有旧值了 entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { trimToSize(maxSize); return createdValue; } }
- trimToSize方法
public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { //如果size没有达到最大缓存maxSize或者map为空,直接返回了,不需要移除操作 if (size <= maxSize || map.isEmpty()) { break; } //获取第一个元素,也就是最老最久没被使用的元素,将它移除。还记得get时会刷新get到元素的位置吧?? Map.Entry<K, V> toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value);//移除后size减少 } entryRemoved(true, key, value, null); } }