LruCache 浅析

简介
LruCache (Least Recently Used) 是安卓中一个关于内存缓存的类,特别是在操作图片的时候,大量的图片会导致oom,所以采用lru,可以保持一个最大size的内存缓存,如果超过这个size,会把最少使用的缓存给移除,以保证内存不会不限增加移除的又是最少使用的缓存。

构造
LruCache 浅析_第1张图片

通过构造可以看到,内部使用的是 LinkedHashMap ,真是这个关键的LinkedHashMap才实现了超出size时把最少使用的移除掉。它是一个双向循环链表,它的每一个数据结点都有两个指针,分别指向直接前驱和直接后继。
构造方法public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)中accessOrder是指定它的排序方式,当它为false时,只按插入的顺序排序,即新放入的顺序会在链表的尾部;而当它为true时,更新或访问某个节点的数据时,这个对应的结点也会被放到尾部。所以一顿操作下来头部的就是最少使用的数据,当size超过设定的最大size时 就从头部开始移除不就可以了吗?那么是不是这样的呢?
LruCache 浅析_第2张图片

继续看源码:

//往缓存加入数据
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++;
            //计算新增后zise大小   safeSizeof  调用sizeof方法,该方法默认返回1,所以在图片缓存时我们要重写该方法,让他计算大小而不是个数。
            size += safeSizeOf(key, value);
            //返回key对应的前一个value
            previous = map.put(key, value);
            //如果key之前有对应的value,则新value会覆盖旧的value,所以size要减去旧的value的大小
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        //真正的计算大小 保证size 的方法
        trimToSize(maxSize);
        return previous;
    }
 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!");
                }

                //如果size小于maxSize  就退出循环
                if (size <= maxSize || map.isEmpty()) {
                    break;
                }
                //关键部分,每次迭代 取出的都是第一个元素然后移除,第一个也是用的最少的那个元素
                Map.Entry toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                //map中移除该元素,同时size减去他的大小,然后再循环判断,知道seze小于maxSize
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

通过上诉源码可以看到LruCache 的原理其实很简单,就是利用LinkedHashMap的数据结构特性,插入和更新访问操作都可以把数据移到队尾,然后size不够用的时候移除队首元素就可以了。

你可能感兴趣的:(LruCache 浅析)