56、Android LruCache源码分析

一、LruCache 是什么玩意?

官方介绍:

缓存有限数量强引用。每次访问的值,它都会被移到队列的头部。当添加一个值到缓存队列是,该队列末尾的值将被删除,并且可能会被回收。

坊间总结:

LruCache用来缓存最近期间最少使用的算法。核心思想就是,优先清除那些最近最少使用的对象

二、LruCache 干啥用哩?

优先清除那些最近最少使用的对象

三、LruCache 咋用呢?

定义图片缓存工具类 BitmapLRUtils

public class BitmapLRUtils extends LruCache<String, Bitmap> {
    private static int mDefaultMaxSize = 10 * 1024 * 1024;// 默认缓存图片大小 10M

    /**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *                the maximum number of entries in the cache. For all other caches,
     *                this is the maximum sum of the sizes of the entries in this cache.
     */
    private BitmapLRUtils(int maxSize) {
        super(maxSize);
    }

    private static BitmapLRUtils sLrUtils;

    public static BitmapLRUtils getInstance() {
        if (sLrUtils == null) {
            sLrUtils = new BitmapLRUtils(mDefaultMaxSize);
        }
        return sLrUtils;
    }

    @Override
    public void resize(int maxSize) {
        // 调整缓存空间最大值
        super.resize(maxSize);
    }

    @Override
    public void trimToSize(int maxSize) {
        // 裁剪空间
        super.trimToSize(maxSize);
    }

    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        // 同一个Key 对应的值被替换修改时
        super.entryRemoved(evicted, key, oldValue, newValue);
    }

    @Override
    protected Bitmap create(String key) {
        // 在根据Key 获得缓存失败后调用,不重新此方法 默认返回 null
        // return super.create(key);
        // 开发者 可以设置一个默认值
        return BitmapFactory.decodeFile("aa/bb/cc.png");
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        //  默认size大小为1
        // return super.sizeOf(key, value);
        // 用户自己计算 保存 Value所需要的空间
        return value.getByteCount();
    }

使用:

   BitmapLRUtils.getInstance().put("http://xxxx.jpg", BitmapFactory.decodeFile(""));
   BitmapLRUtils.getInstance().get("http://xxxx.jpg");
四、LruCache 源码分析?

构造方法 LruCache(int maxSize)

    public LruCache(int maxSize) {
    	// 设置最大缓存大小
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        // 使用LinkedHashMap 保存对象 
        // 为什么使用 LinkedHashMap???
        // 通过key-value 选择map 方便
        // Lru算法增删多,查找少  使用链表结构 Link . 
        //  其他 ...
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

get(V v):获取数据的方法

 public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        // 同步代码块  ,多线程 安全
        synchronized (this) {
        	// 从map中取值
            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.
         */
		 // 下面一坨干嘛用的呢?
		 // create()  默认是返回null , 
		 // 其 被 protected 修饰 说明给子类重写 创建一个默认的值
        V createdValue = create(key);
        // 默认是返回null , 
        if (createdValue == null) {
        	// 通过key,获取vlue为空
            return null;
        }
		
        synchronized (this) {
            createCount++;
            // 保存创建的,发现key 已有映射的值???
            // 那为什么  map.get(key) == null  呢???
            mapValue = map.put(key, createdValue);
			
            if (mapValue != null) {
                // 有一个冲突,所以撤销最后一个put
                map.put(key, mapValue);
            } else {
            	// 添加 vaule的大小
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
        	// mapValue 不为空,则不保存 createdValue
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
        	// 计算裁剪空间大小
            trimToSize(maxSize);
            return createdValue;
        }
    }
// 自定义默认值
protected V create(K key) {
        return null;
    }

put(K k,V v):保存数据

 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  不为null 时 说明key之前有映射的对象
            previous = map.put(key, value);
            if (previous != null) {
          		// 减去 之前有映射的对象占用空间大小
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
        	// 移除回调
            entryRemoved(false, key, previous, value);
        }
		// 计算裁剪空间大小
        trimToSize(maxSize);
        return previous;
    }

trimToSize(int maxSize) 计算空间大小

    private void trimToSize(int maxSize) {
    	// 不断的循环  直到 达到size<=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 没有超出最大缓存大小	 终止!!!
                if (size <= maxSize) {
                    break;
                }

                // BEGIN LAYOUTLIB CHANGE
                // get the last item in the linked list.
                // This is not efficient, the goal here is to minimize the changes
                // compared to the platform version.
                Map.Entry<K, V> toEvict = null;
                // 获取链表中的最后一项。
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }
                // END LAYOUTLIB CHANGE
				// 链表为空
                if (toEvict == null) {
                    break;
                }
			
                key = toEvict.getKey();
                value = toEvict.getValue();
                // 移除value
                map.remove(key);
                // 减少size
                size -= safeSizeOf(key, value);
                evictionCount++;
            }
			// 移除 value
            entryRemoved(true, key, value, null);
        }
    }
五、LruCache 总结?

LruCache
1、通过Linkhashmap做为容器通过Key-value 进行 保存、获取、移除操作
2、如何保证被移除的是最后一个呢?

               Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }

3、如何保证被移除的是最近最少使用的呢?

翻遍源码 也没发现 ,有知道的大佬请指教
最终对比了下:
trimToSize()方法在不同的版本是有不同的实现的。

	// 在android-28 SDK 中,只是获得map集合中的最后一个元素,进行移除
			Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }
 		// 而在其他版本中:
               Map.Entry<K, V> toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }

4、通过Value对象的大小加减到size变量上,再与maxSize 大小比较。判断移除条件是否成立

参考文章:
LruCache缓存使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值