LruCache的Lru指的是LeastRecentlyUsed纱耻,也就是近期最少使用算法险耀。
使用LruCache其實(shí)非常簡(jiǎn)單,下面以一個(gè)圖片緩存為例:
創(chuàng)建LruCache對(duì)象:
private static class StringBitmapLruCache extends LruCache<String, Bitmap> {
public StringBitmapLruCache() {
// 構(gòu)造方法傳入當(dāng)前應(yīng)用可用最大內(nèi)存的八分之一
super((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));
}
@Override
// 重寫(xiě)sizeOf方法甩牺,并計(jì)算返回每個(gè)Bitmap對(duì)象占用的內(nèi)存
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount() / 1024;
}
@Override
// 當(dāng)緩存被移除時(shí)調(diào)用累奈,第一個(gè)參數(shù)是表明緩存移除的原因贬派,true表示被LruCache移除澎媒,false表示被主動(dòng)remove移除搞乏,可不重寫(xiě)
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap
newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
}
@Override
// 當(dāng)get方法獲取不到緩存的時(shí)候調(diào)用戒努,如果需要?jiǎng)?chuàng)建自定義默認(rèn)緩存,可以在這里添加邏輯储玫,可不重寫(xiě)
protected Bitmap create(String key) {
return super.create(key);
}
}
LruCache<String, Bitmap> mLruCache = new StringBitmapLruCache();
把圖片寫(xiě)入緩存:
mLruCache.put(name, bitmap);
從緩存讀取圖片:
mLruCache.get(name);
從緩存中刪除圖片:
mLruCache.remove(name);
源碼分析:
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)造方法中我們獲取了緩存的最大值撒穷,并且創(chuàng)建了一個(gè)LinkedHashMap對(duì)象,這個(gè)對(duì)象就是整個(gè)LruCache的關(guān)鍵端礼,淘汰最少使用的算法入录,其實(shí)就是通過(guò)這個(gè)類(lèi)來(lái)實(shí)現(xiàn)的(HashMap和雙向鏈表合二為一即是LinkedHashMap。所謂LinkedHashMap僚稿,其落腳點(diǎn)在HashMap,因此更準(zhǔn)確地說(shuō)贫奠,它是一個(gè)將所有Entry節(jié)點(diǎn)鏈入一個(gè)雙向鏈表的HashMap。由于LinkedHashMap是HashMap的子類(lèi)唤崭,所以LinkedHashMap自然會(huì)擁有HashMap的所有特性。比如谢肾,LinkedHashMap的元素存取過(guò)程基本與HashMap基本類(lèi)似,只是在細(xì)節(jié)實(shí)現(xiàn)上稍有不同芦疏。當(dāng)然,這是由LinkedHashMap本身的特性所決定的微姊,因?yàn)樗~外維護(hù)了一個(gè)雙向鏈表用于保持迭代順序。此外兢交,LinkedHashMap可以很好的支持LRU算法)。
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++;
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方法中酪穿,先計(jì)算插入的對(duì)象類(lèi)型的大小,調(diào)用的方法是safeSizeOf被济,這個(gè)方法其實(shí)只是簡(jiǎn)單的調(diào)用了我們?cè)跇?gòu)造的時(shí)候重寫(xiě)的sizeOf方法,如果返回負(fù)數(shù)只磷,則拋出異常。接著把我們需要緩存的對(duì)象插入LinkedHashMap中喳瓣,如果緩存中有這個(gè)對(duì)象,就把size復(fù)位畏陕。如果緩存中有這個(gè)key對(duì)應(yīng)的對(duì)象,則調(diào)用entryRemoved方法惠毁,這個(gè)方法默認(rèn)為空犹芹,但是如果我們需要在緩存更新之后進(jìn)行一些記錄的話鞠绰,可以通過(guò)在構(gòu)造時(shí)重寫(xiě)這個(gè)方法來(lái)做到腰埂。接下來(lái)蜈膨,調(diào)用trimToSize方法,這個(gè)方法是去檢查當(dāng)前的size有沒(méi)有超過(guò)maxSize
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 || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
可以看到翁巍,這里的判斷邏輯也很簡(jiǎn)單,通過(guò)不斷的檢查灶壶,如果超過(guò)maxSize,則從LinkedHashMap中剔除一個(gè)驰凛,直到size等于或者小于maxSize,這里同樣會(huì)調(diào)用entryRemoved方法恰响。
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;
}
}
這里可以看到,當(dāng)我們調(diào)用get方法的時(shí)候胚宦,直接從LinkedHashMap中g(shù)et一個(gè)當(dāng)前key的對(duì)象并返回,如果返回的為Null间唉,則會(huì)調(diào)用create方法來(lái)創(chuàng)建一個(gè)對(duì)象利术,而create方法默認(rèn)也是一個(gè)空方法,直接返回null印叁,所以,如果你需要在get失敗的時(shí)候創(chuàng)建一個(gè)默認(rèn)的對(duì)象轮蜕,可以在構(gòu)造的時(shí)候重寫(xiě)create方法。如果重寫(xiě)了create方法跃洛,那么下面的代碼會(huì)被執(zhí)行率触,先進(jìn)行LinkedHashMap的插入方法汇竭,如果已經(jīng)存在,則返回存在的對(duì)象贝搁,否則返回我們創(chuàng)建的對(duì)象。這里可以看到廓握,這里重復(fù)判斷列表中是否已經(jīng)存在相同的對(duì)象,原因是偿枕,如果create方法處理的時(shí)間過(guò)長(zhǎng)户辫,有可能create出來(lái)的對(duì)象已經(jīng)被put到LinkedHashMap中了
remove 方法
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;
}