杂谈(app优化、android机制系列)
杂谈(Lrucache机制)
杂谈(android基础知识点梳理笔记)
杂谈(http / https Socket)
简介
LruCache是Android 3.1所提供的一个缓存类,目前主流的图片加载框架底层大部分都是基于它。
LruCache内部维护了一个LinkedHashMap,利用LinkedHashMap的数据结构特点去实现最近很少使用这一算法进行缓存。
LinkedHashMap介绍
LinkedHashMap的数据结构:散列表+双向链表
特点:双向链表结构可以控制访问顺序和插入顺序,使得LinkedHashMap中的<key,value>对按照一定顺序排列起来。
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
accessOrder:true 则为访问顺序,为false,则为插入顺序
LruCache
public LruCache(int maxSize) {
// ......省略N行代码
// 由此可见内部维护的是一个LinkedHashMap
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
put
public final V put(K key, V value) {
// ......省略N行代码
synchronized (this) {
putCount++;
// safeSizeOf 计算缓存大小
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;
}
- safeSizeOf
// 计算缓存大小
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}
- entryRemoved
//当item被回收或者删掉时调用。改方法当value被回收释放存储空间时被remove调用,
//或者替换item值时put调用,默认实现什么都没做
//evicted true---为释放空间被删除;false---put或remove导致
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
- sizeOf 方法是我们要重写的
protected int sizeOf(K key, V value) {
return 1;
}
- trimToSize
一个死循环,不断地删除LinkedHashMap中队尾的元素,即近期最少访问的,直到缓存大小小于最大值。
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) {
break;
}
//map.eldest(); 返回映射中最老的条目,如果映射为空,则返回null
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
get
调用一次get就相当于访问了一次该元素,将会更新队列,保持整个队列是按照访问顺序排序。这个更新过程就是在LinkedHashMap中的get()方法中完成的。
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//获取对应的缓存对象
//get()方法会实现将访问的元素更新到队列头部的功能
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
// ......省略N行代码
}
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;
}
总结:
- LruCache中维护了一个集合LinkedHashMap。
- LinkedHashMap是以访问顺序排序的(accessOrder=true)
- 当调用put()方法时,就会在集合中添加元素(safeSizeOf来计算缓存大小)
- put时最后调用trimToSize()(内部一个死循环): 判断缓存是否已满,如果满了就用LinkedHashMap的eldest(),返回一个最老元素
- 当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。
- 我们需要重新sizeOf ,entryRemoved也可以重写。