简介
LruCache (Least Recently Used) 是安卓中一个关于内存缓存的类,特别是在操作图片的时候,大量的图片会导致oom,所以采用lru,可以保持一个最大size的内存缓存,如果超过这个size,会把最少使用的缓存给移除,以保证内存不会不限增加移除的又是最少使用的缓存。
构造
通过构造可以看到,内部使用的是 LinkedHashMap ,真是这个关键的LinkedHashMap才实现了超出size时把最少使用的移除掉。它是一个双向循环链表,它的每一个数据结点都有两个指针,分别指向直接前驱和直接后继。
构造方法public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)中accessOrder是指定它的排序方式,当它为false时,只按插入的顺序排序,即新放入的顺序会在链表的尾部;而当它为true时,更新或访问某个节点的数据时,这个对应的结点也会被放到尾部。所以一顿操作下来头部的就是最少使用的数据,当size超过设定的最大size时 就从头部开始移除不就可以了吗?那么是不是这样的呢?
继续看源码:
//往缓存加入数据
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++;
//计算新增后zise大小 safeSizeof 调用sizeof方法,该方法默认返回1,所以在图片缓存时我们要重写该方法,让他计算大小而不是个数。
size += safeSizeOf(key, value);
//返回key对应的前一个value
previous = map.put(key, value);
//如果key之前有对应的value,则新value会覆盖旧的value,所以size要减去旧的value的大小
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
//真正的计算大小 保证size 的方法
trimToSize(maxSize);
return previous;
}
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!");
}
//如果size小于maxSize 就退出循环
if (size <= maxSize || map.isEmpty()) {
break;
}
//关键部分,每次迭代 取出的都是第一个元素然后移除,第一个也是用的最少的那个元素
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
//map中移除该元素,同时size减去他的大小,然后再循环判断,知道seze小于maxSize
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
通过上诉源码可以看到LruCache 的原理其实很简单,就是利用LinkedHashMap的数据结构特性,插入和更新访问操作都可以把数据移到队尾,然后size不够用的时候移除队首元素就可以了。