universalimageloader 的 MemoryCache 源码分析

背景

前段时间稍微修改了下我们图片框架的最大缓存值,昨天优化组的同事找到了我说现在内存变的很大,然后带着一些疑惑还是再次仔细的阅读了下 universalimageloader 的 MemoryCache 源码。

universalimageloader 的 MemoryCache 分类与简单功能介绍:

其类图如下(来源网路):

功能:
  • LruMemoryCache:lru算法,强引用
  • LimitedAgeMemoryCache:超出时间则删除,强引用
  • FuzzyKeyMemoryCache:同一个图片新的尺寸会覆盖缓存中该图片老的尺寸。
  • WeakMemoryCache:弱引用
  • LimitedMemoryCache:强和弱引用,设置的内存超出16M会有个log警告,未超出容量用强引用,超出和未超出的都会用软引用,下面几个都是它的子类都包含它的特性。
    • FIFOLimitedMemoryCache:先进先出的缓存策略,当超过缓存限定值,
      先删除最先加入缓存的bitmap
    • LRULimitedMemoryCache:lru算法,弱引用,当超过缓存限定值,
      先删除最近最少使用的bitmap
    • LargestLimitedMemoryCache:当超过缓存限定值,
      先删除最大的bitmap
    • UsingFreqLimitedMemoryCache:当超过缓存限定值,
      先删除使用次数最少的bitmap

源码分析:

下面主要分析 LimitedMemoryCache 与其相关子类或者父类源码,其他像 LruMemoryCache 这种就是利用了下 LinkedHashMap 的 Lru 特性而已。

LimitedMemoryCache:
最大缓存:

我们在使用 LimitedMemoryCache 或者其子类时,需要在构造中设定一个最大允许的缓存值,这个缓存值其实是最大的强引用的缓存大小。

可以看到如果超出 MAX_NORMAL_CACHE_SIZE(16M) 会有一个警告。

put:


首先会检查当前需要存入的 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 是由子类实现的,看了最上面的类图后我们也知道了它的子类有哪些。

LRULimitedMemoryCache 的 removeNext
@Override
    protected Bitmap removeNext() {
        Bitmap mostLongUsedValue = null;
        synchronized (lruCache) {
            Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
            if (it.hasNext()) {
                Entry<String, Bitmap> entry = it.next();
                mostLongUsedValue = entry.getValue();
                it.remove();
            }
        }
        return mostLongUsedValue;
    }

LRULimitedMemoryCache removeNext 的时候就是利用 LinkedHashMap 含有的 LRU 特性移除一个即可,这就实现了 LRU 的效果。

LargestLimitedMemoryCache 的 removeNext
@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;
    }

LargestLimitedMemoryCache removeNext 的时候就是去遍历查找缓存(强引用)里面最大的一个bitmap, 最后将它删除即可。

FIFOLimitedMemoryCache 的 removeNext
@Override
    protected Bitmap removeNext() {
        return queue.remove(0);
    }

FIFOLimitedMemoryCache 看名字就知道了, first in first out,所以里面只需要维护一个队里就可以了,每次需要 removeNext 的时候干掉头部的即可。

UsingFreqLimitedMemoryCache 的 removeNext
@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;
    }

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 一次对应的就加一。

总结

根据不同的场景选用不同的缓存模式还是有必须的,既然要选择那就有必要弄清楚每个选项的优劣。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值