欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于Android內(nèi)存緩存LruCache的使用及其源碼解析

 更新時間:2023年05月31日 08:30:06   作者:_小馬快跑_  
LruCache作為內(nèi)存緩存,使用強(qiáng)引用方式緩存有限個數(shù)據(jù),當(dāng)緩存的某個數(shù)據(jù)被訪問時,它就會被移動到隊列的頭部,本文詳細(xì)介紹了關(guān)于Android內(nèi)存緩存LruCache的使用及其源碼解析,需要的朋友可以參考下

整體介紹

LruCache 作為內(nèi)存緩存,使用強(qiáng)引用方式緩存有限個數(shù)據(jù),當(dāng)緩存的某個數(shù)據(jù)被訪問時,它就會被移動到隊列的頭部,當(dāng)一個新數(shù)據(jù)要添加到LruCache而此時緩存大小要滿時,隊尾的數(shù)據(jù)就有可能會被垃圾回收器(GC)回收掉,LruCache使用的LRU(Least Recently Used)算法,即:把最近最少使用的數(shù)據(jù)從隊列中移除,把內(nèi)存分配給最新進(jìn)入的數(shù)據(jù)。

  • 如果LruCache緩存的某條數(shù)據(jù)明確地需要被釋放,可以覆寫entryRemoved(boolean evicted, K key, V oldValue, V newValue)
  • 如果LruCache緩存的某條數(shù)據(jù)通過key沒有找到,可以覆寫 create(K key),這簡化了調(diào)用代碼,即使錯過一個緩存數(shù)據(jù),也不會返回 null,而會返回通過create(K key) 創(chuàng)造的數(shù)據(jù)。
  • 如果想限制每條數(shù)據(jù)的緩存大小,可以覆寫 sizeOf(K key, V value) ,如下面設(shè)置bitmap最大緩存大小是4MB:
 int cacheSize = 4 * 1024 * 1024; // 4MiB
 LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
     protected int sizeOf(String key, Bitmap value) {
         return value.getByteCount();
     }
 }

LruCache 這個類是線程安全的。自動地執(zhí)行多個緩存操作通過synchronized 同步緩存:

 synchronized (cache) {
   if (cache.get(key) == null) {
       cache.put(key, value);
   }
 }

LruCache不允許 null 作為一個 key 或 value,get(K)、put(K,V),remove(K) 中如果key 或 value 為 null,都會拋出異常:throw new NullPointerException("key == null || value == null")。

常用API

方法備注
void resize(int maxSize)更新存儲大小
V put(K key, V value)存數(shù)據(jù),返回之前key對應(yīng)的value,如果沒有,返回null
V get(K key)取出key對應(yīng)的緩存數(shù)據(jù)
V remove(K key)移除key對應(yīng)的value
void evictAll()清空緩存數(shù)據(jù)
Map<K, V> snapshot()復(fù)制一份緩存并返回,順序從最近最少訪問到最多訪問排序

使用示例

  • 初始化:
 private Bitmap bitmap;
 private String STRING_KEY = "data_string";
 private LruCache<String, Bitmap> lruCache;
 private static final int CACHE_SIZE = 10 * 1024 * 1024;//10M
 lruCache = new LruCache<String, Bitmap>(CACHE_SIZE) {
    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
       super.entryRemoved(evicted, key, oldValue, newValue);
    }
    @Override
    protected int sizeOf(String key, Bitmap value) {
        //這里返回的大小用單位kb來表示的
        return value.getByteCount() / 1024;
    }
};

最大緩存設(shè)置為10M,key是String類型,value設(shè)置的是Bitmap

  • 存數(shù)據(jù):
  if (lruCache!=null){
     lruCache.put(STRING_KEY, bitmap);
   }
  • 取數(shù)據(jù):
 if (lruCache != null) {
     bitmap = lruCache.get(STRING_KEY);
  }
  • 清除緩存:

源碼分析

定義變量、構(gòu)造器初始化

    //LruCache.java
    private final LinkedHashMap<K, V> map;//使用LinkedHashMap來存儲操作數(shù)據(jù)
    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;//緩存數(shù)據(jù)大小,不一定等于元素的個數(shù)
    private int maxSize;//最大緩存大小
    private int putCount;// 成功添加數(shù)據(jù)的次數(shù)
    private int createCount;//手動創(chuàng)建緩存數(shù)據(jù)的次數(shù)
    private int evictionCount;//成功回收數(shù)據(jù)的次數(shù)
    private int hitCount;//查找數(shù)據(jù)時命中次數(shù)
    private int missCount;//查找數(shù)據(jù)時未命中次數(shù)
    /**
     * @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<K, V>(0, 0.75f, true);
    }

構(gòu)造函數(shù)中初始化了 LinkedHashMap,參數(shù)maxSize指定了緩存的最大大小。

修改緩存大小

/**
 * Sets the size of the cache.
 *
 * @param maxSize The new maximum size.
 */
public void resize(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }
    synchronized (this) {
        this.maxSize = maxSize;
    }
    trimToSize(maxSize);
}

resize(int maxSize) 用來更新大小,先是加同步鎖更新maxSize的值,接著調(diào)用了trimToSize()方法:

/**
 * 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!");
            }
            //如果已經(jīng)小于最大緩存,則無需接著往下執(zhí)行了
            if (size <= maxSize) {
                break;
            }
            //拿到最近最少使用的那條數(shù)據(jù)
            Map.Entry<K, V> toEvict = map.eldest();
            if (toEvict == null) {
                break;
            }
            key = toEvict.getKey();
            value = toEvict.getValue();
            //從LinkedHashMap移除這條最少使用的數(shù)據(jù)
            map.remove(key);
            //緩存大小size減去移除數(shù)據(jù)的大小,如果沒有覆寫sizeOf,則減去的值是1
            size -= safeSizeOf(key, value);
            evictionCount++;
        }
        entryRemoved(true, key, value, 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;
}
/**
 * Returns the size of the entry for {@code key} and {@code value} in
 * user-defined units.  The default implementation returns 1 so that size
 * is the number of entries and max size is the maximum number of entries.
 *
 * <p>An entry's size must not change while it is in the cache.
 */
 protected int sizeOf(K key, V value) {
     return 1;
 }
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

trimToSize() 循環(huán)移除最近最少使用的數(shù)據(jù)直到剩余緩存數(shù)據(jù)的大小等于小于最大緩存大小。 注:我們看到sizeOf()entryRemoved()都是protected來修飾的,即可以被覆寫,如果sizeOf()沒有被覆寫,那么變量size 代表的是緩存數(shù)據(jù)的數(shù)量,maxSize代表的是最大數(shù)量,如果覆寫sizeOf(),如:

 @Override
  protected int sizeOf(String key, BitmapDrawable value) {
      return value.getBitmap().getByteCount() / 1024;
  }

此時size 代表的是緩存數(shù)據(jù)的大小,maxSize代表的是最大緩存大小。

存數(shù)據(jù)

/**
 * 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) {
    //key或value為空直接拋異常
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }
    V previous;
    synchronized (this) {
        //添加數(shù)據(jù)次數(shù)+1
        putCount++;
         //緩存數(shù)據(jù)的大小增加
        size += safeSizeOf(key, value);
         //添加緩存數(shù)據(jù),添加之前如果key值對應(yīng)的value不為空,則newValue會覆蓋oldValue,并返回oldValue;
         //如果key值對應(yīng)的value為空,則返回Null
        previous = map.put(key, value);
        if (previous != null) {
           //之前的oldValue不為空,則在緩存size中減去oldValue
            size -= safeSizeOf(key, previous);
        }
    }
    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }
    //重新檢查緩存大小
    trimToSize(maxSize);
    return previous;
}

調(diào)用 LinkedHashMapput(key, value) 添加緩存數(shù)據(jù)后,在添加之前如果 key 值對應(yīng)的value 不為空,則 newValue 會覆蓋 oldValue ,并返回 oldValue;如果 key 值對應(yīng)的 value 為空,則返回 null,接著根據(jù)返回值來重新設(shè)置緩存 size 和最大緩存 maxSize 的大小。

取數(shù)據(jù)

 /**
  * 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) {
         //通過key取數(shù)據(jù)
         mapValue = map.get(key);
         if (mapValue != null) {
             //如果取到了數(shù)據(jù),命中次數(shù)+1
             hitCount++;
             return mapValue;
         }
         //沒有取到數(shù)據(jù),未命中此時+1
         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.
      */
     //如果沒有覆寫create(),默認(rèn)create()方法返回的null
     V createdValue = create(key);    
     if (createdValue == null) {
         return null;
     }
     //如果覆寫了create(),即根據(jù)key值手動創(chuàng)造了value,則繼續(xù)往下執(zhí)行
     synchronized (this) {
          //創(chuàng)造數(shù)據(jù)次數(shù)+1
         createCount++;
         //嘗試將數(shù)據(jù)添加到緩存中
         mapValue = map.put(key, createdValue);
         if (mapValue != null) {
             // There was a conflict so undo that last put
             //mapValue不為空,說明之前的key對應(yīng)的是有數(shù)據(jù)的,那么就跟我們手動創(chuàng)建的數(shù)據(jù)沖突了,
             //所以執(zhí)行撤消操作,重新把mapValue添加到緩存中,用mapValue去覆蓋createdValue
             map.put(key, mapValue);
         } else {
             //如果mapValue為空,說明之前的key值對應(yīng)的value確實為空,我們手動添加createdValue后,
             //需要重新計算緩存size的大小
             size += safeSizeOf(key, createdValue);
         }
     }
     if (mapValue != null) {
         entryRemoved(false, key, createdValue, mapValue);
         return mapValue;
     } else {
         trimToSize(maxSize);
         return createdValue;
     }
 }
 protected V create(K key) {
      return null;
  }

取數(shù)據(jù)的流程大致是這樣:

  • 首先通過 map.get(key) 來嘗試取出 value,如果value存在,則直接返回value;
  • 如果value不存在,則執(zhí)行下面的create(key),如果create()沒有被覆寫,則直接返回null;
  • 如果 create() 被覆寫了,即通過 key 值創(chuàng)建了一個createdValue,那么嘗試通過mapValue = map.put(key, createdValue)createdValue 添加到緩存中去,mapValueput()方法的返回值,mapValue代表的是key之前對應(yīng)的值,如果mapValue不為空,說明之前的key對應(yīng)的是有數(shù)據(jù)的,那么就跟我們手動創(chuàng)建的數(shù)據(jù)沖突了,所以執(zhí)行撤消操作,重新把mapValue添加到緩存中,用mapValue去覆蓋createdValue,最后再重新計算緩存大小。

移除數(shù)據(jù)

 /**
  * Removes the entry for {@code key} if it exists.
  *
  * @return the previous value mapped by {@code key}.
  */
 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;
 }

調(diào)用 map.remove(key) 通過 key 值刪除緩存中 key 對應(yīng)的value,然后重新計算緩存大小,并返回刪除的value

其他一些方法:

      //清空緩存
     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;
     }
     /**
      * Returns a copy of the current contents of the cache, ordered from least
      * recently accessed to most recently accessed.
      */
      //復(fù)制一份緩存,順序從最近最少訪問到最多訪問排序
     public synchronized final Map<K, V> snapshot() {
         return new LinkedHashMap<K, V>(map);
     }

以上就是關(guān)于Android內(nèi)存緩存LruCache的使用及其源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Android LruCache的使用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論