Android 缓存(1)---内存缓存LruCache

来源:互联网 发布:万达电商 网络没说过 编辑:程序博客网 时间:2024/05/22 06:30

前言

LruCache中:LRU是指Least Recently Used,即最近最少使用算法,Cache就是是缓存了。

  • 缓存其实就是本地的一个备份,读取网络后,先把图片下载到本地,然后从本地加载到容器。这样下一次再加载这张图的时候,就不需要从网络读取了,直接从本地加载。省流量不说,用户体验也会更好。
  • Lru策略的目的其实就是如果缓存空间存满了,又需要往里添加,就删掉最近没有用到的。

用法

创建实例:

//当前进程可用容量int maxMemory = (int) Runtime.getRuntime().maxMemory();//缓存空间一般为总量的1/8int cacheSize = maxMemory/8;lruCache = new LruCache<String, Bitmap>(cacheSize){    //获取bitmap大小    @Override    protected int sizeOf(String key, Bitmap value) {        return value.getByteCount()/1024;    }    //缓存删除的时候调用    @Override    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {    super.entryRemoved(evicted, key, oldValue, newValue);    }};

写入图片缓存

lruCache.put(key,bitmap);

读取图片缓存

lruCache.get(key)

源码

构造函数

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);    }

设置最大缓存值
创建LinkedHashMap对象,其中前面两个参数就是HashMap构造函数需要的参数,后面的true表明LinkedHashMap按照访问的次序来排序。

  • 按照访问的次序来排序的含义:当调用LinkedHashMap的get(key)或者put(key, value)时,碰巧key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。
  • 按照插入顺序来排序的含义:调用get(key), 或者put(key, value)并不会对线性结构产生任何的影响。

这个类是实现Lru算法的关键,有兴趣的可以看看这个类的原理。

get

/**     * Returns the value for {@code key} if it exists in the cache or can be     * created by {@code #create}. If a value was returned, it is moved to the     * head of the queue. This returns null if a value is not cached and cannot     * be created.     */    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++;                return mapValue;            }            missCount++;        }        /*         * Attempt to create a value. This may take a long time, and the map         * may be different when create() returns. If a conflicting value was         * added to the map while create() was working, we leave that value in         * the map and release the created value.         */        V createdValue = create(key);        if (createdValue == null) {            return null;        }        synchronized (this) {            createCount++;            mapValue = map.put(key, createdValue);            if (mapValue != null) {                // There was a conflict so undo that last put                map.put(key, mapValue);            } else {                size += safeSizeOf(key, createdValue);            }        }        if (mapValue != null) {            entryRemoved(false, key, createdValue, mapValue);            return mapValue;        } else {            trimToSize(maxSize);            return createdValue;        }    }

13-20行中:如果map取出来的值不为null,直接返回。
14行:调用了linkHashMap的get方法。

public V get(Object key) {        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);        if (e == null)            return null;        e.recordAccess(this);        return e.value;    }

其中重点为recordAccess

void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            //如果是访问排序            if (lm.accessOrder) {                lm.modCount++;                //移除                remove();                //添加到对列尾部                addBefore(lm.header);            }        }

29行:否则 重新创建
30-32:如果创建的为null,返回null。这个create(key)在我们没有重写的时候默认返回null。
36行:将createdValue插入缓存。
38-40行:如果不为null,则证明之前插入的key有对应的数据,因此撤销插入操作,重新将mapValue插入,替代之前插入的createdValue。发生这种情况的主要原因是在29行的创建操作比较耗时,在创建好之前就已经插入了。
42行:如果mapValue==null,则证明此前key没有对应value,36行的插入是没有问题的。因此需要重新计算大小。
46-48行:调用entryRemoved,返回mapValue
50-51行:重新调整缓存大小,超过限定值则移除不常用的缓存,并返回createdValue。

put

 /**     * Caches {@code value} for {@code key}. The value is moved to the head of     * the queue.     *     * @return the previous value mapped by {@code key}.     */    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++;            size += safeSizeOf(key, value);            previous = map.put(key, value);            if (previous != null) {                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, value);        }        trimToSize(maxSize);        return previous;    }

15行:添加前测量缓存大小,safeSizeOf(key, value)实际上调用我们初始化时的sizeOf()方法。
16行:插入缓存。
17-19行:如果已经存在,删除缓存
26行:重新调整缓存大小,超过限定值则移除不常用的缓存

trimToSize

/**     * Remove the eldest entries until the total of remaining entries is at or     * below the requested size.     *     * @param maxSize the maximum size of the cache before returning. May be -1     *            to evict even 0-sized elements.     */    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!");                }                if (size <= maxSize || map.isEmpty()) {                    break;                }                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();                key = toEvict.getKey();                value = toEvict.getValue();                map.remove(key);                size -= safeSizeOf(key, value);                evictionCount++;            }            entryRemoved(true, key, value, null);        }    }

18-20行:如果缓存小于设定的最大值,或为null,直接break。
22-27行:删除第一个缓存