LruCache 是什么
Lru(Least Recently Used)近期最少使用算法是将近期最少使用的数据从缓存中移除。它主要是用于取代 SoftReference 的。其实 LruCache 内部仅仅是对 LinkedHashMap 的封装,Lru 也是由 LinkedHashMap 实现的。 LruCache 中做的主要操作是设置缓存大小,然后在往缓存放数据的时候,如果超过所设置的大小,就将不常用的数据移除掉。
源码解析
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
/**
* @param maxSize 指缓存的大小,默认是缓存的个数
* 如果是 bitmap 的话,就要重写 sizeOf 方法,用于返回一个 bitmap 的大小,
* 而 maxsize 就是所缓存的 bitmap 的总大小。
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
//重点看下 get 方法
public final V get(K key) {
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
// 在 map 中找到了就直接返回
return mapValue;
}
missCount++;
}
/**
*缓存中没找到的话就调用 create 去创建(这一步由子类实现),
*這個方法沒加锁,具体可以看下面该方法的注释
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
//create 完成了
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// mapValue 不为 null 说明在 create 的时候,该值(mapValue)已经被添加到缓存了
// 那么就保留 mapValue,将 createValue 丢弃
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
/**
* put 方法没什么特别的,需要注意的是它是将值放到队列头部
*/
public final V put(K key, V value) {
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;
}
/**
* 缓存如果超过了最大值,则将旧数据移除
*/
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
// 如果 maxSize 很小(如 -1),那么就会清空缓存
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
/**
* 该方法没加锁(因为创建的方法一般不涉及到多个线程访问共享变量的问题),那么多个线程
* 可以同时请求相同的数据的情况是存在的。那么就有了上面 get() 方法中 if (mapValue != null) 的判断
*/
protected V create(K key) {
return null;
}
}
LruCache 类的核心代码就是上面那么多,可以看出和 Lru 算法没什么关系,我们看下简单的用法:
// 一般图片缓存会使用最大内存的1/4或1/8
val maxMemory = (Runtime.getRuntime().maxMemory() / 4) as Int
val imgCache = object : LruCache<String, Bitmap>(maxMemory) {
override fun sizeOf(key: String, value: Bitmap): Int {
return value.byteCount
}
}
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher);
imgCache.put("${R.mipmap.ic_launcher}$", bitmap)