简介
key-value结构。
一个持有有限数量元素强引用的缓存机制。
每次访问元素之后把它移动到序列的头部。cache已满的时候添加元素,序列尾部的元素就会被删除(释放引用)并可被GC。这就是最近最少使用的特性。LruCache保留频繁使用的元素,淘汰非频繁使用的元素。
如果被缓存的元素持有需要被显式释放的资源,那么覆写
entryRemoved
函数去实现,因为删除只是代表释放引用,保存的数据可能需要a.release()去进行自己的释放。如果有这样的需求:get的时候没有即使命中元素也希望有元素返回,那么可以覆写
create
函数实现。-
cache的size默认是按照key-value的数量决定的。如果想要按照内存大小去决定size,可以通过覆写
sizeOf
函数去实现。例如缓存4MB内存的bitmap:int cacheSize = 4 * 1024 * 1024; // 4MiB LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }}
线程安全。
在
get
的时候key不能为空,在put
的时候key和value都不能为空。如果get
,put
或者remove
返回空,那么很明显:该key不在缓存中。LruCache中封装了LinkedHashMap,LruCache加上了控制缓存大小的逻辑,实际上是通过LinkedHashMap保存数据的。
基本原理
LruCache中有个LinkedHashMap类型的成员变量map,在LruCache的构造函数中将会初始化map变量。map=new LinkedHashMap(0,0.75f,true)
,实例化了一个以访问顺序来排序元素的LinkedHashMap对象。LruCache只是加上了控制缓存大小的逻辑,实际上数据是由LinkedHashMap保存的。
最近最少使用的特性是如何实现的?
由LinkedHashMap保证此特性,在实例化LinkedHashMap时指定accessOrder参数为true。调用LinkedHashMap的put
或者get
函数都会把该元素插入(或者移动)到序列的尾部。LruCache每次put
的时候会检查当前的大小是否超过了限制,是的话就会调用trimToSize
去把最近最少使用的元素淘汰掉。
trimToSize(int maxSize)
private 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) {//1.
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.
Map.Entry<K, V> toEvict = null;
for (Map.Entry<K, V> entry : map.entrySet()) {
toEvict = entry;
}
// END LAYOUTLIB CHANGE
if (toEvict == null) {//2.
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
trimToSize是个死循环,以下两种情况会退出循环:
当前的容量size<maxSize;
LruCache中没有元素。
这个函数是实现LruCache元素数量限制的关键。它会把LinkedHashMap的尾部数据,即不“活跃”的数据,remove掉,从而来实现元素数量限制的特性。