LruCache是Android3.1提供的一个缓冲类,support包中也有。它对数据的存储采用了近期最少使用算法。
Android开发中,如网络加载图片,如果不进行缓存,那么流量的消耗和体验是很差的。并且Android系统有对每个应用施加了内存限制,一旦超出限制,就看见了常见的OOM的报错。所以我们需要一个有缓存策略的类LruCache,来存放这些图片。
我们从源码来看看,这个高大上的东西如何实现的。
int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
int cacheSize = maxMemory/8;
LruCache mCache = new LruCache(cacheSize){
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
};
};
private final LinkedHashMap map;
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;//当前内存大小
private int maxSize;//最大内存大小
private int putCount;//加入成功就加一,一下的count,分析源码发现没用到,就只是记录一下
private int createCount;//创建成功加一
private int evictionCount;//清除一次加一
private int hitCount;//成功查找到一次加一
private int missCount;//get但不存在
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75f, true);
}
传入我们设定的最大大小,然后new 了一个LinkedHashMap。并且看第三个参数为true。看API知道了,true的时候,LinkedHashMap遵循LRU算法。具体LinkedHashMap怎么实现的,下面会浅析一下,先看LruCache,一步步来。表急 。。
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
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;
}
注意看代码中标红的三句代码:
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
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);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
其中标红的create()方法,也是可以重写的。作用就是get不到东西时,如果重写并返回值,就默认创建。不重写此方法,返回null,则不会默认创建。创建成功,都会执行trimTosize()方法。下面我们看看这个关键的方法。
public 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) {
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);
}
}
看看这几句标红的代码。死循环,直到当前大小小于大小限制,才停止,否则一直map.eldest(),从linkedHashMap获取到链表数据,删除掉。
@Override public V get(Object key) {
if (key == null) {
HashMapEntry e = entryForNullKey;
if (e == null)
return null;
if (accessOrder)
makeTail((LinkedEntry) e);
return e.value;
}
int hash = secondaryHash(key);
HashMapEntry[] tab = table;
for (HashMapEntry e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
if (accessOrder)
makeTail((LinkedEntry) e);
return e.value;
}
}
return null;
}
其他代码和咱要研究的关系不大,看标红的,accessOrder,这个LruCache构造器中设为了true,表示使用LRU算法,那么接下来,一切谜团都在makeTail()方法中了。
private void makeTail(LinkedEntry e) {
// Unlink e
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
// Relink e as tail
LinkedEntry header = this.header;
LinkedEntry oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
代码很少,这段代码在当初大学时学链表时,被搞的快 了。目的就是,切断原链表的header和tail,
插入新的entry到header 的前面,就是说是循环链表的尾部。
public Entry eldest() {
LinkedEntry eldest = header.nxt;
return eldest != header ? eldest : null;
}
耶!明白了吧,获取了header 的下一项。