前段时间稍微修改了下我们图片框架的最大缓存值,昨天优化组的同事找到了我说现在内存变的很大,然后带着一些疑惑还是再次仔细的阅读了下 universalimageloader 的 MemoryCache 源码。
下面主要分析 LimitedMemoryCache 与其相关子类或者父类源码,其他像 LruMemoryCache 这种就是利用了下 LinkedHashMap 的 Lru 特性而已。
我们在使用 LimitedMemoryCache 或者其子类时,需要在构造中设定一个最大允许的缓存值,这个缓存值其实是最大的强引用的缓存大小。
可以看到如果超出 MAX_NORMAL_CACHE_SIZE(16M) 会有一个警告。
首先会检查当前需要存入的 bitmap 大小是否小于设置的最大允许值,如果不小于那边是不会加入到强引用的,而是直接走 super.put ,super.put 其实就是直接把这个 bitmap 对象存入了弱引用,这个弱引用没设置大小。如果当前bitmap 的值小于设置的最大允许值那么才会进入一系列的检查与强引用,它可能会循环删除强引用里面的bitmap,让强引用里面的总大小一直小于我们设置的最大允许值。
可以看到里面可能会调用到 removeNext
方法,这是一个抽象方法,你可以实现不同的策略。
其四个子类都复写了 put
方法,但是都是首先调用了 super.put()
,然后再根据自己的缓存特性做不同的操作。如下面的 LargestLimitedMemoryCache
:
@Override
public boolean put(String key, Bitmap value) {
if (super.put(key, value)) {
valueSizes.put(value, getSize(value));
return true;
} else {
return false;
}
}
这里就存入了 valueSizes
,key 是一个 bitmap ,value 是这个bitmap 的大小,因为 LargestLimitedMemoryCache
等下如要删除的时候需要找到最大的 bitmap,所以就有必要为每个 bitmap 做一个映射。其他几个子类原理也差不多,只是实现不一样。
如上面所说 removeNext
是由子类实现的,看了最上面的类图后我们也知道了它的子类有哪些。
removeNext
:@Override
protected Bitmap removeNext() {
Bitmap mostLongUsedValue = null;
synchronized (lruCache) {
Iterator> it = lruCache.entrySet().iterator();
if (it.hasNext()) {
Entry entry = it.next();
mostLongUsedValue = entry.getValue();
it.remove();
}
}
return mostLongUsedValue;
}
LRULimitedMemoryCache removeNext
的时候就是利用 LinkedHashMap
含有的 LRU 特性移除一个即可,这就实现了 LRU 的效果。
removeNext
:@Override
protected Bitmap removeNext() {
Integer maxSize = null;
Bitmap largestValue = null;
Set> entries = valueSizes.entrySet();
synchronized (valueSizes) {
for (Entry entry : entries) {
if (largestValue == null) {
largestValue = entry.getKey();
maxSize = entry.getValue();
} else {
Integer size = entry.getValue();
if (size > maxSize) {
maxSize = size;
largestValue = entry.getKey();
}
}
}
}
valueSizes.remove(largestValue);
return largestValue;
}
LargestLimitedMemoryCache removeNext
的时候就是去遍历查找缓存(强引用)里面最大的一个bitmap, 最后将它删除即可。
removeNext
:@Override
protected Bitmap removeNext() {
return queue.remove(0);
}
FIFOLimitedMemoryCache
看名字就知道了, first in first out,所以里面只需要维护一个队里就可以了,每次需要 removeNext 的时候干掉头部的即可。
removeNext
:@Override
protected Bitmap removeNext() {
Integer minUsageCount = null;
Bitmap leastUsedValue = null;
Set> entries = usingCounts.entrySet();
synchronized (usingCounts) {
for (Entry entry : entries) {
if (leastUsedValue == null) {
leastUsedValue = entry.getKey();
minUsageCount = entry.getValue();
} else {
Integer lastValueUsage = entry.getValue();
if (lastValueUsage < minUsageCount) {
minUsageCount = lastValueUsage;
leastUsedValue = entry.getKey();
}
}
}
}
usingCounts.remove(leastUsedValue);
return leastUsedValue;
}
UsingFreqLimitedMemoryCache 的 removeNext
是每次干掉使用次数最少的,也是去循环变量存有 bitmap 与其使用次数的 map ,然后找到最少次数的,最后移除掉。
我们来看下次数是怎么累加的
@Override
public Bitmap get(String key) {
Bitmap value = super.get(key);
// Increment usage count for value if value is contained in hardCahe
if (value != null) {
Integer usageCount = usingCounts.get(value);
if (usageCount != null) {
usingCounts.put(value, usageCount + 1);
}
}
return value;
}
非常简单,就是每 get 一次对应的就加一。
根据不同的场景选用不同的缓存模式还是有必须的,既然要选择那就有必要弄清楚每个选项的优劣。