LruCache缓存

QuibblerQuibbler 2023-10-25 约 11 分钟 1650 次阅读

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 实现

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w