android中記憶體快取是如何實現的
阿新 • • 發佈:2019-01-30
那就有必要來看看LruCache原始碼了
裡面有一個重要的資料結構LinkedHashMap。具體講解在這裡(http://blog.csdn.net/lxj1137800599/article/details/54974988)
在此總結一下用法:
1.新增一個數據。先找到陣列中對應的index,然後把資料放到連結串列的最後位置。由於是雙向連結串列,那麼就等於放在header.prv
2.獲取一個數據。先找到陣列中對應的index,然後找到資料所在的位置。如果是按照讀取順序來排序的,那麼還要將這個節點放到雙向連結串列的最後一位(這個特性,可以實現LRU演算法)
public class LruCache<K, V> {
//map用來儲存外界的快取物件
private final LinkedHashMap<K, V> map;
// 建構函式
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
//設定accessOrder為true,按照讀取順序來儲存快取
this.map = new LinkedHashMap<K, V>(0 , 0.75f, true);
}
//獲取一個快取物件
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//設定為true了,還要將這個節點放到雙向連結串列的最後一位
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=null,size擴大然後trimToSize
//如果是替換資料,mapValue!=null
mapValue = map.put(key, createdValue);
if (mapValue != null) {
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
//新增一個快取物件
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);
// previous = null表示新新增的快取之前未存在過
// previous != null表示之前已存在資料
if (previous != null) {
// 之前已有資料,那麼size再減回去
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
//重點在這裡****************************************
//如果超出最大容量,那就去掉header.next
//由於新新增的資料已經跑到header.prv去了,所以刪除的必定是最近最少使用的
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);
}
}
}
總結如下:accessOrder設定為true。
當新增快取時,先新增資料,再把對應的entry挪到雙向連結串列的末尾。如果size超過最大值,就刪除header.next
當獲取快取時,先獲取資料。由於設定為true,那麼也會將對應的entry挪到雙向連結串列的末尾