LRUCache:最近最少使用(Least Recently Used)缓存,可用于bitmap
的图片缓存。
具体策略就是:以一个LinkedHashMap来存放元素,LinkedHashMap设置以访问时间存储。在每次存的时候,判断如果size超过设定的最大尺寸maxSize,就删除掉最不常用的元素,直到满足不大于maxSize。
1)对于bitmap,可以设maxSize=4M,通过重写sizeOf方法返回bitmap的真正字节大小。
2)可以重写create方法(默认实现return null),创建出key对应的value对象,这样每次get的时候,如果元素不存在,就通过create创建,并将创建的元素加入map,此时如果产生了冲突,就用旧的元素替换掉创建的元素。然后调整map的size大小。
源码说明如下:
初始化LinkedHashMap,传入的第三个参数true。表示我们在访问获取的时候,会根据时间进行排序。
public LruCache(int maxSize) {
...
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
public final V get(K key) {//通过key来获取存储的数据
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);//其实是通过LinkedHashMap来获取的
if (mapValue != null) {
hitCount++; //这里时在记录每次获取数据成功的次数
return mapValue;
}
missCount++;//这里记录获取数据失败的次数
}
V createdValue = create(key);//如果当前没有这个数据,则调用create创建
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;//更新自己创建的数量
mapValue = map.put(key, createdValue);//将新创建的数据加入到LinkedHashMap里面
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);//如果冲突来再次尝试
} else {
size += safeSizeOf(key, createdValue);//更新当前已经存储数据的数量
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);//调用entryRemoved通知已经清理掉了一个数据
return mapValue;
} else {
trimToSize(maxSize);//检测是否已经满了
return createdValue;
}
}
调整map大小,判断是否需要移除
public void trimToSize(int maxSize) {
while (true) {//一个死循环:直到当前LruCache没有满才会跳出这个循环
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {//异常错误检测:当前已经缓存的数据数量不能为负数,并且没有数据的时候其数据数量不能大于0
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {//如果已经缓存的数据数量小于最大容量(没有满),则跳出循环
break;
}
Map.Entry<K, V> toEvict = map.eldest();//通过LinkedHashMap来获取最不常用的一个键值对数据
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key); //删除这个数据
size -= safeSizeOf(key, value); //获取这个删除的键值对数据所占用的容量(有可能是1,也有可能是计算的图片占用的内存,也有可能是其他)
evictionCount++;
}
entryRemoved(true, key, value, null); //回调用entryRemoved,一般用于通知客户端,这个键值对数据已经被清理出LruCache了
}
}
可供重写的函数
protected V create(K key) {
return null;
}
protected int sizeOf(K key, V value) {
return 1;
}
return null;
}