Android中LruCach的原理



public class LruCache<K, V> {
    //緩存 map 集合,爲什麼要用LinkedHashMap
    //因爲沒錯取了緩存值之後,都要進行排序,以確保
    //下次移除的是最少使用的值
    private final LinkedHashMap<K, V> map;
    //當前緩存的值
    private int size;
    //最大值
    private int maxSize;
    //添加到緩存中的個數
    private int putCount;
    //創建的個數
    private int createCount;
    //被移除的個數
    private int evictionCount;
    //命中個數
    private int hitCount;
    //丟失個數
    private int missCount;

    //實例化 Lru,需要傳入緩存的最大值
    //這個最大值可以是個數,比如對象的個數,也可以是內存的大小
    //比如,最大內存只能緩存5兆
    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);
    }

    //重置最大緩存的值
    public void resize(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }

        synchronized (this) {
            this.maxSize = maxSize;
        }
        trimToSize(maxSize);
    }

    //通過 key 獲取緩存值
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }


        //如果沒有,用戶可以去創建
        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            createCount++;
            mapValue = map.put(key, createdValue);

            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                //緩存的大小改變
                size += safeSizeOf(key, createdValue);
            }
        }
        //這裏沒有移除,只是改變了位置
        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            //判斷緩存是否越界
            trimToSize(maxSize);
            return createdValue;
        }
    }

    //添加緩存,跟上面這個方法的 create 之後的代碼一樣的
    public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        trimToSize(maxSize);
        return previous;
    }

    //檢測緩存是否越界
    private void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }
                //如果沒有,則返回
                if (size <= maxSize) {
                    break;
                }
                //以下代碼表示已經超出了最大範圍
                Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }

                if (toEvict == null) {
                    break;
                }
                //移除最後一個,也就是最少使用的緩存
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

    //手動移除,用戶調用
    public final V remove(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            previous = map.remove(key);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }
    //這裏用戶可以重寫它,實現數據和內存回收操作
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}


    protected V create(K key) {
        return null;
    }

    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }

        //這個方法要特別注意,跟我們實例化 LruCache 的 maxSize 要呼應,怎麼做到呼應呢,比如 maxSize 的大小爲緩存的個數,這裏就是 return 1就 ok,如果是內存的大小,如果5M,這個就不能是個數 了,這是應該是每個緩存 value 的 size 大小,如果是 Bitmap,這應該是 bitmap.getByteCount();
    protected int sizeOf(K key, V value) {
        return 1;
    }

    //清空緩存
    public final void evictAll() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }


    public synchronized final int size() {
        return size;
    }


    public synchronized final int maxSize() {
        return maxSize;
    }


    public synchronized final int hitCount() {
        return hitCount;
    }


    public synchronized final int missCount() {
        return missCount;
    }


    public synchronized final int createCount() {
        return createCount;
    }


    public synchronized final int putCount() {
        return putCount;
    }


    public synchronized final int evictionCount() {
        return evictionCount;
    }


    public synchronized final Map<K, V> snapshot() {
        return new LinkedHashMap<K, V>(map);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章