LruCache源码的理解

LruCache源码的理解

使用场景

在Android手机上加载图片,一般会用到三级缓存策略
这里写图片描述

内存的缓存策略,一般会用到LruCache来解决

内存用于缓存遇到的问题

 1. 手机给每个应用分配的内存空间是有限的,如果内存中数据量过大,会造成OOM,所以,我们需要适当的清理内存
 2. 在清理内存的时候,我们希望最老的部分最先被清除,从而让出空间来

LruCache中使用LinkedHashMap可以解决的问题

1. LinkedHashMap可以当做一个容器来使用
2. LinkedHashMap是一个Map,可以用key来给数据做标记
3. LinkedHashMap支持访问顺序,每次get()或者add()一个数据,都会把相应的数据放到链表的尾部,这样,链表头部的数据就是没怎么用过的数据,也就是最老的数据

图形解释

点击这里查看大图
这里写图片描述

RTFRS解释

public class LruCache<K, V> {

    //正真用于缓存的容器
    //支持访问顺序呢:指在迭代遍历列表中的元素时最近访问的元素会排在LinkedHashMap的尾部
    //清除操作是从头部开始清除的,可以实现“最近最少使用”的需求
    private final LinkedHashMap<K, V> map;

    //当前换粗的大小
    private int size;

    //最大容量
    private int maxSize;

    //put进来的数量
    private int putCount;

    //create出来的数量
    private int createCount;

    //清除的数量
    private int evictionCount;

    //get到值的数量
    private int hitCount;

    //没有get到值得数量
    private int missCount;

    //构造方法
    public LruCache(int maxSize) {
        //异常的处理
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        //设置最大容量
        this.maxSize = maxSize;
        //初始化容器
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

    //重新设置最大容量
    public void resize(int maxSize) {
        //异常处理
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }

        //更新最大容量
        synchronized (this) {
            this.maxSize = maxSize;
        }
        //size的值可能变小,尝试清除部分缓存
        trimToSize(maxSize);
    }

    //通过key来获取相应的值
    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的数据
                hitCount++;
                return mapValue;
            }
            //当容器中没有储存相应的值,那么:更新missCount的数据
            missCount++;
        }


        //create一个值,默认为null
        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }
        //重写create()方法后执行下面的代码


        synchronized (this) {
            //create出了一个值,那么:更新createCount的数据
            createCount++;
            //放入容器,map.put()的返回值:
            //1.null,新添加的数据
            //2.不为null,表示链表中存在相应的value,put()表示更新数据,将旧的数据返回
            mapValue = map.put(key, createdValue);

            //对mapValue的判断表示put()的操作是更新还是添加
            if (mapValue != null) {
                //更新操作,那么create出来的值不能覆盖以前的值
                //将旧的值重新放回到容器中
                map.put(key, mapValue);
            } else {
                //添加进来新的值,那么容器的大小会发生变化,更新size的值
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            //默认什么都没有做
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            //size变大,尝试清除部分缓存
            trimToSize(maxSize);
            return createdValue;
        }
    }

    //添加到缓存中
    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的数据
            putCount++;
            //size变化,更新size的值
            size += safeSizeOf(key, value);
            //添加到容器中
            previous = map.put(key, value);
            //跟get()中的原理一样,这里判断是更新还是添加
            if (previous != null) {
                //更新操作,那么size多记录一份旧的value的大小,减去相应的值
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            //默认什么都没有做
            entryRemoved(false, key, previous, value);
        }

        //size可能变大,尝试清除部分缓存
        trimToSize(maxSize);
        return previous;
    }


    //尝试清除部分缓存
    public void trimToSize(int maxSize) {
        //无限循环,与if (size <= maxSize)配合:可以将缓存清空到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) {
                    break;
                }

                //获取链表的第一个条目
                Map.Entry<K, V> toEvict = map.eldest();
                //没有缓存下数据,结束循环
                if (toEvict == null) {
                    break;
                }

                //移除第一个条目
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                //更新size的数据
                size -= safeSizeOf(key, value);
                //这里执行了一次清除,那么,更新evictionCount的值
                evictionCount++;
            }

            //默认什么都没有做
            entryRemoved(true, key, value, null);
        }
    }

    //移除相应的缓存
    public final V remove(K key) {
        //异常处理
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            //移除
            previous = map.remove(key);
            //判断是否真的从缓存中移除了
            if (previous != null) {
                //从缓存中移除了,更新size的数据
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            //默认什么都没有做
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }


    //默认什么都没有做;如果有相应的需求,可以重写该方法
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}


    //create出null;如果有相应的需求,可以重写该方法
    protected V create(K key) {
        return null;
    }

    //判断大小
    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }

    //开放人员需要重写的方法,用来标记每个item对应的大小
    protected int sizeOf(K key, V value) {
        return 1;
    }


    //获取各种成员变量的方法
    //...

    //这里获取的是一个map的副本
    public synchronized final Map<K, V> snapshot() {
        return new LinkedHashMap<K, V>(map);
    }

    //不解释
    @Override public synchronized final String toString() {
        int accesses = hitCount + missCount;
        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
                maxSize, hitCount, missCount, hitPercent);
    }
}

结语

通过对LruCache的理解,实际上,我们是更加了解了冷门的LinkedHashMap的一些功能:可以记录访问顺序

转载请标明出处:http://blog.csdn.net/qq_26411333/article/details/51523790

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值