-
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); } }