Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android LRUCache解析

android LRUCache解析

編輯:關於Android編程

LRU(Least Recently Used)最近最少使用算法

原理

緩存保存了一個強引用(Android 2.3開始,垃圾回收器更傾向於回收弱引用和軟引用,軟引用和弱引用變得不可靠,Android 3.0中,圖片的數據會存儲在本地的內存當中,因而無法用一種可預見的方式將其釋放)限制值的數量. 每當值被訪問的時候,它會被移動到隊列的頭部. 當緩存已滿的時候加入新的值時,隊列中最後的值會出隊,可能被回收LRUCache內部維護主要是通過LinkedHashMap實現

這是一個安全的線程,多線程緩存通過同步實現?

使用

默認情況下,緩存的大小是由值的數量決定,重寫sizeOf計算不同的值

如果你緩存值需要明確釋放,重寫entryRemoved()

int maxMemory = (int) Runtime.getRuntime().maxMemory();    
int mCacheSize = maxMemory / 8;  
//給LruCache分配1/8 4M  
mMemoryCache = new LruCache(mCacheSize){  

  //必須重寫此方法,來測量Bitmap的大小  
  @Override  
  protected int sizeOf(String key, Bitmap value) {  
          return value.getRowBytes() * value.getHeight();  
  }  

};
mMemoryCache.put(key, bitmap)
mMemoryCache.get(key)

這個類不允許有空的鍵值. get,put,remove 返回空值,key對應的值不在緩存中

源碼分析

構造函數,初始化了最大容量和LinkedHashMap

 /**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap(0, 0.75f, true);
    }

這裡將LinkedHashMap最後一個參數(accessOrder)設置為true,將accessOrder設置為true時,可以使遍歷順序和訪問順序一致,其內部雙向鏈表將會按照近期最少訪問到近期最多訪問的順序排列Entry對象

put方法,首先不允許鍵值為空,然後是線程安全,put的次數加一,size增加,以鍵值對的形式存入LinkedHashMap,如果之前已經存在了這個鍵值對,size減少成原來的大小,如果容量超過maxsize,將會刪除最近很少訪問的entry

/**
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     *
     * @return the previous value mapped by {@code key}.
     */
    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;
    }

put方法有一個很關鍵的地方超過最大值是會刪除最近最少訪問的

trimToSize首先線程安全,檢查當前大小是否大於最大值,如果大於最大值,從LinkedHashMap中去除最近最少(循環刪除鏈表首部元素)被訪問的元素,獲得鍵值,刪除

/**
     * Remove the eldest entries until the total of remaining entries is at or
     * below the requested size.
     *
     * @param maxSize the maximum size of the cache before returning. May be -1
     *            to evict even 0-sized elements.
     */
    public 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 toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

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

get方法,首先key不能為空,線程安全,根據key,從LinkedHashMap中獲得value,不為空的話返回,為空的話,創建一個key,創建失敗返回null,創建成功,在LinkedHashMap中創建鍵值對,存在就覆蓋,不存在size增加,返回value值

/**
     * Returns the value for {@code key} if it exists in the cache or can be
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created.
     */
    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++;
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */

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

核心代碼分析完畢,想知道LinkedHashMap,請聽下回哔哔 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved