上一篇对于universal image loader源码分析——图片内存缓存做了大概的分析,本篇将对具体的图片内存缓存策略进行具体的分析。
1.FIFOLimitedMemoryCache
除了继承自LimitedMemoryCache限制了内存大小外,它使用的是FIFO先进先出策略。上源码:
所有,图片添加的时候是添加在queue的最后的,而移除则是移除第一个!这就是先进先出策略,先加入的先被移除。至于什么时候移除?当然是超过最大内存限制的时候哇!
2.LRULimitedMemoryCache
除了继承自LimitedMemoryCache限制了内存大小外,使用的是LinkedHashMap链表来存储强引用缓存,而对于所有图片使用弱引用缓存,就并没有其他特点了。
3.LargestLimitedMemoryCache
最大尺寸策略,继承自LimitedMemoryCache,无疑是优先移除图片内存占据最大空间的图片。
所以在进行存储的时候,需要存储该图片缓存的大小:
@Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { valueSizes.put(value, getSize(value)); return true; } else { return false; } }
从它的移除方法便可以看出:
@Override protected Bitmap removeNext() { Integer maxSize = null; Bitmap largestValue = null; Set<Entry<Bitmap, Integer>> entries = valueSizes.entrySet(); synchronized (valueSizes) { for (Entry<Bitmap, Integer> 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; }
4.UsingFreqLimitedMemoryCache
使用频率缓存策略,继承自LimitedMemoryCache,即优先移除使用频率低的图片缓存。
所以,存储时存储的value则是使用频率了:
@Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { usingCounts.put(value, 0); return true; } else { return false; } }
每当使用一次,即调用get方法一次,则加1:
@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; }
移除时,则移除使用频率最低的即可:
@Override protected Bitmap removeNext() { Integer minUsageCount = null; Bitmap leastUsedValue = null; Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet(); synchronized (usingCounts) { for (Entry<Bitmap, Integer> 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; }
5.FuzzyKeyMemoryCache
关键字匹配缓存策略(暂时这么理解吧),直接实现了MemoryCache接口。根据注释我们可以知道,这个类一般用的很少,使用场景是:一图多尺寸缓存,可能理解起来会麻烦点。我们一般的一图多尺寸Url拼接是这样的:url_720x1280,也就是说,当key的前半部分相同时,作为同一个bitmap进行存储,而之前的bitmap则被直接移除。
从构造方法我们可以看出,需要传递一个比较器规则给它:
public FuzzyKeyMemoryCache(MemoryCache cache, Comparator<String> keyComparator) { this.cache = cache; this.keyComparator = keyComparator; }用于存储时进行比较,若相等则直接先移除再进行存储,说实话,在图片很多的时候,这么去遍历的话确实不太好,本来列表图片就对性能比较敏感。
@Override public boolean put(String key, Bitmap value) { // Search equal key and remove this entry synchronized (cache) { String keyToRemove = null; for (String cacheKey : cache.keys()) { if (keyComparator.compare(key, cacheKey) == 0) { keyToRemove = cacheKey; break; } } if (keyToRemove != null) { cache.remove(keyToRemove); } } return cache.put(key, value); }而在源码中,有这么个默认的比较器:
private static final String URI_AND_SIZE_SEPARATOR = "_";
public static Comparator<String> createFuzzyKeyComparator() { return new Comparator<String>() { @Override public int compare(String key1, String key2) { String imageUri1 = key1.substring(0, key1.lastIndexOf(URI_AND_SIZE_SEPARATOR)); String imageUri2 = key2.substring(0, key2.lastIndexOf(URI_AND_SIZE_SEPARATOR)); return imageUri1.compareTo(imageUri2); } }; }
在imageload默认初始化的时候使用的便是上面这个比较器规则:
private void initEmptyFieldsWithDefaultValues() {
...
if (denyCacheImageMultipleSizesInMemory) {
memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
}
...
}
6.LimitedAgeMemoryCache
时长限制缓存策略,直接实现了MemoryCache接口。也就是说当图片存储的时间,超过了初始化缓存时设定的时间,则进行移除。
从其构造方法我们可以看出,传递的时间参数单位是秒:
public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) { this.cache = cache; this.maxAge = maxAge * 1000; // to milliseconds }
在这个缓存策略中,有个特别之处就是,它里面有个cache,也就是说,它可以在其他缓存策略的基础上,再添加一套缓存机制。
在存储的时候,将当前的时间进行存储:
@Override public boolean put(String key, Bitmap value) { boolean putSuccesfully = cache.put(key, value); if (putSuccesfully) { loadingDates.put(key, System.currentTimeMillis()); } return putSuccesfully; }获取的时候,判断是否超时移除该缓存:
@Override public Bitmap get(String key) { Long loadingDate = loadingDates.get(key); if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) { cache.remove(key); loadingDates.remove(key); } return cache.get(key); }
7.LruMemoryCache
@Override public final boolean put(String key, Bitmap value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } synchronized (this) { size += sizeOf(key, value); Bitmap previous = map.put(key, value); if (previous != null) { size -= sizeOf(key, previous); } } trimToSize(maxSize); return true; }