使用强引用来缓存有限数量的值.每次被访问的值,将会移动到队列的头部。当队列大小超过缓存大小时,队列尾部的值将会被删除并且允许垃圾回收器将它回收。
如果你需要明确知道缓存数据什么时候被释放,需要重写 entryRemoved();方法
如果出现缓存缺失时,可以重写create();方法。使用这种简单的方法,可以确保永远存在返回值。
默认的,缓存大小是根据缓存项数量来计算的。可以重写sizeOf()方法来设置不同的计算缓存大小的单位。例如用于缓存4MiB的图片:
int cacheSize = 4 * 1024 * 1024;//用于计算缓存总Size
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize){
protected int sizeOf(String key, Bitmap value){
return value.getByteCount();//返回每一张图片Size
}
}
这个类是线程安全的,多个缓存的原子操作代码:
syschronized(cache){
if(cache.get(key) == null){
cache.put(key, value);
}
}
这个类不允许
null
作为key或者value。如果get()
,put()
和remove()
方法中返回值为null
,那么就明确表明,缓存中不存在这个值。这个类在Anroid3.1加入,如果需要在之前版本中使用,请使用support包中程序。
成员变量
private final LinkedHashMap<K, V> map;//Cache 缓存的主要是实现基础。
private int size;//基于某个单位上的缓存大小,不一定等于缓存成员数量。具体数量和size的转换,通过方法sizeOf()实现。
private int maxSize;//初始化时传入的,缓存最大值。
private int putCount;//put()方法调用次数。
private int createCount;//create()方法调用次数。
private int evictionCount;//超出限制,被删除的数量。
private int hitCount;//返回get()方法正常获取缓存数据次数。
private int missCount;//返回get()方法为在缓存中获取数据的次数。
构造方法
public LruCache(int maxSize) {
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
在构造函数中,初始化了成员变量中的LinkedHashMap()。LRUCache主要实现感觉更多的是LinkedHashMap的学习。
LinkedHashMap是HashMap增加一个双向链表。 所以LinkedHashMap可以插入顺序或者访问顺序访问。
LinkedHashMap构造参数中最后一个参数accessOrder,用来判断是使用哪种顺序。accressOrder等于true为访问顺序,
等于false 为数据插入顺序。
LinkedHashMap重写了HashMap的Entry。增加了before和after,使Entry虽然存在HashMap中但是另外也存在一个双向链表。因此LinkedHashMap可以方便的通过访问和插入顺序来访问某个Entry。也正是因为LinkedHashMap存在这个访问数据顺序序,所以用它来做LRUCache,方便删除最早之前加入的数据,并且可以根据访问顺序来调整链表顺序。
关于LinkedHashMap推荐博客:http://blog.csdn.net/justloveyou_/article/details/71713781
get()方法
public final V get(K key) {
V mapValue;
synchronized (this) {
//从map中查找数据
mapValue = map.get(key);
if (mapValue != null) {
//存在则直接返回。并且查找成功计数变量hitCount++
hitCount++;
return mapValue;
}
//查找失败计数变量missCount++
missCount++;
}
/*
* 下面尝试使用 create()方法创建一个新值,加到到map中。而且如果新值存在冲突,将会保留以前的值。
* create()方法默认返回值为null。
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
//将创建的值添加到map中,并返回key以前对应的value。
mapValue = map.put(key, createdValue);
//key以前存在对应值,则将以前的值重新添加到map中,舍弃新创建值。
//否则将size增加新建value 所占size。
if (mapValue != null) {
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
//mapValue不为null,使用mapValue替换了createValue,调用entryRemoved()。
//为null,则重新计算是否越界。
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
put方法
/**
* 将键值对插入到队列头部。
* @return 返回以前与这个key相对应的Value。
*/
public final V put(K key, V value) {
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value); //当前总size加上新加入Entry的size
previous = map.put(key, value); //加入Map中
//如果以前与key相对应value不为空,那么size减去那个value占据size。
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
//因为删除了previous 所以调用entryRemoved()方法。
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
remove()方法
/**
* 移除被 key对应的entry,如果不存在返回null。
*/
public final V remove(K key) {
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;
}
resize()方法和trimToSize()方法和evictAll()
/**
* 设置缓存size
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
synchronized (this) {
this.maxSize = maxSize;
}
trimToSize(maxSize);
}
/**
* 删除超出MaxSize的最早加入的entry。
*/
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size <= maxSize) {
break;
}
//返回map中最古老的entry,如果map为空则返回null。
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
//从map中移除最古老的entry
map.remove(key);
//size进行相应的减少
size -= safeSizeOf(key, value);
//修改被移除次数
evictionCount++;
}
//因为最古老的entry的呗删除了,所以调用entryRemoved().
entryRemoved(true, key, value, null);
}
}
/**
* 清空缓存
*/
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
}
部分protected 方法
/**
* 返回entry中每个value对应的在总size中占据的大小。上面说明中通过重写该方法,
* 实现一个用于缓存Bitmap的LruCache
*/
protected int sizeOf(K key, V value) {
return 1;
}
/**
*当entry被remove或者被put方法替换或者被删除最古老值时 调用此方法
*evicted 为true为删除掉最古老值时调用 ;false为remove()或者put()方法中调用。
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
/**
*当调用get方法,没有与key相对的值时调动。
*/
protected V create(K key) {
return null;
}