概述
LruCache用于数据缓存中,当缓存所占空间超过了为LruCahce分配的最大空间时,它会回收最近最少使用的元素所占的内存空间,以达到内存平衡的目的。LruCache的底层实现是LinkedHashMap有序双联表。
1.LruCache的创建
long maxSize=Runtime.getRuntime().totalMemory()/1024;
int lruCacheSize=(int) maxSize/8;
LruCache<String, Bitmap> lruCache=new LruCache<String, Bitmap>(lruCacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight();
}
};
//LinkedHashMap
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);
}
2.LruCache.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++;
//1.首先获取存放key,value时内存空间,累加到目前所占空间size中
size += safeSizeOf(key, value);
//2.将key/value保存到LinkedHashMap有序集合中 ,返回集合key对应之前的value
previous = map.put(key, value);
//3.如果集合中存在value,所占空间size不变,仅仅更改key对应的value值
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
//4.如何集合存在对应的value则回调entryRemoved方法,这是空方法,需要开发者自行实现
if (previous != null) {
entryRemoved(false, key, previous, value);
}
//6.调用trimeToSize方法检查是否超出缓存大小,并作出响应
trimToSize(maxSize);
return previous;
}
entryRemoved回调
* @param evicted true if the entry is being removed to make space, false
* if the removal was caused by a {@link #put} or {@link #remove}.
* @param newValue the new value for {@code key}, if it exists. If non-null,
* this removal was caused by a {@link #put}. Otherwise it was caused by
* an eviction or a {@link #remove}.
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
trimToSize检查缓存是否超越maxSize并清理最近最少使用元素
ate void trimToSize(int maxSize) {
while (true) {//1.不断循环检测
K key;
V value;
synchronized (this) {//2.参数校验
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
//3.如果已使用的缓存空间没有达到maxSize阀值则跳出循环
if (size <= maxSize) {
break;
}
// BEGIN LAYOUTLIB CHANGE
// get the last item in the linked list.
// This is not efficient, the goal here is to minimize the changes
// compared to the platform version.
//4.如果已使用的空间达到了maxSize,取出LinkedHashMap中最近最少使用的元素,即链表中最后存放的元素
Map.Entry<K, V> toEvict = null;
for (Map.Entry<K, V> entry : map.entrySet()) {
toEvict = entry;
}
// END LAYOUTLIB CHANGE
//5.如果链表中最后的元素为空则跳出循环
if (toEvict == null) {
break;
}
/*6.取出链表中最后一个元素调用LinkedHashMap进行删除
* 并且修改已使用缓存空间size的值
*/
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
//7.回调entryRemoved方法交给调用者实现,注意这里返回的evicted为true
entryRemoved(true, key, value, null);
}
}
3.LruCache.get获取缓存数据操作
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//1.实际调用的是LinkedHashMap.get(key)方法
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
LinkedHashMap.get(key)
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
//还记得LruCache构造LinkedHashMap传的参数accessOrder=true吗?基于访问顺序
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
LinkedHashMap.afterNodeAccess(e)
:将调用get访问的元素放到链表头部位置,这就是最近最少使用的原理
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
网友评论