LruCache的基本使用
在使用LruCache時,一般需要重寫sizeOf方法,該方法用于傳回一個對象所占用的記憶體大小
//擷取系統配置設定給每個應用程式的最大記憶體
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize=maxMemory/8;
private LruCache<String, Bitmap> mMemoryCache;
//給LruCache配置設定1/8
mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){
//重寫該方法,來測量Bitmap的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight()/1024;
}
};
LruCache的構造函數
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);
}
可以看到c的本質是維護了一個LinkedHashMap,我們看下LinkedHashMap的構造函數的第三個參數的含義
/**
* Constructs an empty <tt>LinkedHashMap</tt> instance with the
* specified initial capacity, load factor and ordering mode.
*
* @param initialCapacity the initial capacity 初始化大小
* @param loadFactor the load factor 加載因子
* @param accessOrder the ordering mode - <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order
* //如果是true代表是通路順序,如果是false代表插入順序
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
LruCache是通過維護一個根據通路順序排序的LinkedHashMap
put方法
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++;
//safeSizeOf調用是sizeOf,需要我們重寫,用于計算對象的大小
size += safeSizeOf(key, value);//總的大小加上新的
previous = map.put(key, value);//調用LindedHashMap的put,向map中加入緩存對象,若對象已存在,傳回已有的值,如果不存在則傳回null
if (previous != null) {
size -= safeSizeOf(key, previous);//如果對象已經存在,需要減掉上一個的
}
}
// entryRemoved預設是空實作,我們根據需要自行實作,用于對象删除之後的操作
if (previous != null) {
entryRemoved(false, key, previous, value);
}
// 調整緩存的大小
trimToSize(maxSize);
return previous;
}
put方法主要是調用LindedHashMap的put來儲存資料,調整緩存大小。并調用trimToSize來判斷緩存是否已經滿了,如果大于了最大的緩存大小,通過循環不斷删除最近最少使用的對象,直到緩存大小小于最大的緩存大小
trimToSize方法
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
// 如果緩存小于0或者map為空而緩存大小不為0,抛出異常
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
//沒有超過最大的緩存大小,不需要做任何操作,直接跳出循環
if (size <= maxSize) {
break;
}
//從緩存map中找到最近最少使用的對象,如果不存在直接跳出循環
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);//map中删除這個對象
size -= safeSizeOf(key, value);//調整目前緩存大小
evictionCount++;//回收次數+1
}
//預設空實作
entryRemoved(true, key, value, null);
}
}
trimToSize主要維護緩存size的大小,確定緩存小于最大的緩存值。一旦發現大于最大緩存值,調用map的eldest方法找到最近最少使用的對象,并删除。eldest是擷取LinkedHashMap的隊首的元素。
get方法
public final V get(K key) {
if (key == null) {// 參數合法性檢查
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);//根據key的到對象的value
//如果對象存在,則傳回這個value
if (mapValue != null) {
hitCount++;//命中數+1
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.
* 如果通過key從緩存集合中擷取不到緩存資料,則嘗試使用creat方法創造一個value。
* create預設實作是傳回null,需要的時候可以重寫這個方法
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
// 如果建立成功,則将新的value添加到map中,跟put方法類似
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;
}
LruCache的get方法,從集合中擷取緩存,本質是調用LinkedHashMap的get,每調用一次get就代表通路了一次該元素,将會更新隊列,保持整個隊列是按照通路順序排序。下面是LinkedHashMap的get的實作
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);//将e移到隊尾,保證按照通路順序排序
return e.value;
}
總結
LruCache中維護了一個集合LinkedHashMap,LinkedHashMap是以通路順序排序的。
主要的核心方法put、get和trimToSize。put和get是調用LinkedHashMap的put和get。通過trimToSize確定緩存小于最大緩存值。