Android中的LruCache的原理和使用


Android中的LruCache的原理和使用

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依次做了這么幾件事:

  1. 判空,即不允許key/value為null
  2. 總容量size增加上計算傳入的K-V大小
  3. 將傳入的K-V存入LinkedHashMap
  4. 如過LinkedHashMap中已存在相同K,總容量size減去替換掉的K-VOld大小
  5. 通知VOld被替換(子類實現entryRemoved以監聽)
  6. 比較總容量size和最大容量maxSize,若大於maxSize則循環移除LinkedHashMap頭結點(即最久未被訪問的結點)直至size小於等於maxSize

獲取

當調用get方法獲取存儲內容時,LruCache依次做了這些事:

  1. 判空
  2. 從LinkedHashMap中取出與K對應的V值並返回。如果子類未實現create方法以達到當緩存未命中時創建並存入新V的話,返回null,get流程結束。
  3. 通過create創建VNew,並嘗試把VNew存儲到LinkedHashMap中,流程類似存儲過程,不同之處在於當K沖突時,會舍棄掉新創建的VNew。不要奇怪為什么明明上面通過K取V的時候沒取到,這里卻會K沖突,因為LruCache為了性能考慮(防止子類自定義的create方法耗時過長影響get方法執行性能),只對從LinkedHashMap中取值的過程做了同步處理,這樣在多線程的情況下就可能出現A線程在create的時候,B線程已經將K-VB存入了map。
  4. 返回上面創建的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");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM