LruCache缓存

Quibbler 6月前 291

LruCache缓存


        学过计算机操作系统的都应该指定LRU(Least Recently Used),LRU的意思就是近期最少使用算法,它的核心思想就是会优先淘汰那些近期最少使用的缓存对象。

        Android开发中经常要用到缓存,其中的三级缓存,主要就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LRU算法。Android应用缓存数据分有两种形式

        数据缓存:主要用于保存业务的数据,如接口的返回数据

        图片缓存:主要用于保存应用程序的图片对象,图片在程序里使用比较频繁,而且比较占有网络资源。



1、LruCache

        好在Android系统自Android 3.1 (Honeycomb MR1)开始就为我们提供了LruCache工具类,位于android.util包中,内部封装了实现了LRU算法,使用LinkedHashMap保存键Key和数据Value。一般用MD5来当做key,而不是直接用字符串。MD5生成参考MD5:Java String生成MD5

        LruCache构造函数传入缓存区域上限maxSize

    /**
     * @param maxSize 对于不重写sizeOf(K key,V value)方法的缓存来说,就是所缓存的最大条目数量。
     *                对于重写了sizeOf(K key,V value)方法的缓存来说,就是实际所能缓存的最大数量。
     */
    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);
    }



2、LruCache中的方法

        LruCache源码不到400行,可以很快的看明白其中的实现原理,甚至可以自己仿写一个类似的LruCache功能。不仅要会灵活的使用,还要懂得其中的思想。


2.1、int sizeOf(K key, V value)

        该方法的默认实现返回每条缓存大小1

    /**
     * Returns the size of the entry for {@code key} and {@code value} in
     * user-defined units.  The default implementation returns 1 so that size
     * is the number of entries and max size is the maximum number of entries.
     *
     * <p>An entry's size must not change while it is in the cache.
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }

        一般情况下需要重写该方法,确定每个缓存的大小。这样的话sizeOf()方法才能返回每个具体缓存实际的大小,而不是缓存数量。也不一定必须重写该方法,看情况,我就在开发中没有重写该方法,因为用存储最大的条目来限制,而非总共分配的内存大小来限制。


2.2、size()

        返回实际缓存数据的大小,如果没有重写sizeOf()方法的话,每条缓存数据大小都是“1”,缓存条目数量putCount和缓存大小size就是一样的。

    public synchronized final int size() {
        return size;
    }


2.3、maxSize()

        该方法返回缓存最大容量,也就是开始创建LruCache是传入的maxSize

    public synchronized final int maxSize() {
        return maxSize;
    }


2.4、put(K key, V value)

        数据放入缓存,并且调用trimToSize()不断删除超出大小的元素,直到缓存小于最大值。

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

        看源码,会发现,其实每次put数据进来都会使用safeSizeOf(key,value)计算本次缓存的数据大小并统计。


2.5、entryRemoved()

        特殊情况下需要重写该方法,每当存储的条目被remove或者移除掉,需要执行比如释放资源的操作就可以重写该方法。比如Bitmap缓存,当缓存的Bitmap条目不再使用被移除的时候,可以重写该方法,是否Bitmap底层资源。

    /**
     * Called for entries that have been evicted or removed. This method is
     * invoked when a value is evicted to make space, removed by a call to
     * {@link #remove}, or replaced by a call to {@link #put}. The default
     * implementation does nothing.
     */
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
    	if( null != oldValue){
    		//释放Bitmap
    		oldValue.recycle();
    	}
    }


2.6、private safeSizeOf(K key, V value)

        为什么会有这个二次封装呢?我猜Android可能对重写sizeOf()方法的"蹩脚"程序员不放心,再次检验一下数据是否合法。否则内部会错乱。

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



3、简单使用

        一般用LruCache缓存图片等资源,如下:

    // 缓存最多4MiB的图片
    int cacheSize = 4 * 1024 * 1024;
    
    LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) 
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount();
          
    };

        注意:必须重写sizeOf()方法计算每个缓存Bitmap的大小。否者的话会闹笑话,搞出OOM问题,此时maxSize意思是最多可以缓存4*1024*1024个Bitmap条目。



相关博客:

        Bitmap的缓存(LruCache,DiskLruCache)

        LruCache 的使用及原理

        完全解析Andorid的缓存机制LruCache

        什么是LRUCache 和 LRUCache 实现


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com