LruCache:
LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使
用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的
对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
强引用:直接对象的引用
弱引用:当一个对象只有一个弱引用存在时,此对象随时会被GC回收
软引用:当一个对象只有一个软引用存在时,当系统内存不足时,会被GC回收
LruCache使用:
//获取系统内存
int maxMemory = (int) (Runtime.getRuntime().totalMemory() / 1024);
//设置缓存内存为系统内存的1/8
int cacheSize=maxMemory/8;
LruCache lruCache = new LruCache(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
}
};
这段代码主要是确定LruCache的缓存大小,重写SizeOf是确定图片的大小。(单位要统一)
原理:
LruCache的核心就是维护一个缓存对象列表,其中列表对象的缓存排序为顺序排序,就是一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。而这个队列是由LinkHashMap来维护的,它是一个数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序。这样的特性可以让LinkHashMap可以实现顺序排序。
分析LinkHashMap:看一下LinkHashMap的构造函数
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
其中当参数 accessOrder=true,为排序,为false时为插叙。
那么LinkHaspMap,又是如何添加,获取还有删除的?
看下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++;
//增加缓存对象的大小
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;
}
看一下 trimToSize()方法:
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
//如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {
break;
}
//若如果超过缓存的操作
Map.Entry 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);
}
}
trimToSize()方法,就是一添加就会去循环判断缓存是否已满,不满则跳出循环,否则就删除最先访问的对象。
看一下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++;
}
点进去get();
public V get(Object key) {
Node e;
if ((e = getNode(hash(key), key)) == null)
return null;
//排序
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
由此可见LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以
访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用
trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾
元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用
LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。