LruCache是android提供的一个常用而且有用的数据缓存工具类,通其中是通过键值对来保存数据.对于LruCache首先看看原文的说明:

A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.

其大意是:LruCache是一个通过强引用来保存一定数量的数据的缓存技术.每次使用一个数据时候,这个数据就会被移动到队列的最前面(这样维护来一个访问数据的顺序).当新加一个数据到这个已经满了的缓存时候,这个缓存里面最后面的数据(最不常使用的数据)就会被清理掉.

通过上面的说明,基本可以知道LruCache作用和实现方法,不过仅仅上面的说明要深刻理解其中奥秘还是优点困难.上面有四处标记红色的地方,下面通过对这四处红色文字做以下说明.

(1)"一定数量的数据": 这里的"一定数量"通常是指LruCache缓存数据的数量(个数),也就是说LruCache缓存的键值对数据的个数.注意,这里我说的是"通常".当然也有其他情况:也可以是缓存数据所占用的内存的size(其单位一般时k,当然byte都可以),这个时候,你就需要重写LruCache的这个方法:sizeOf(K key, V value)来计算每一个键值对数据所占用的内存:

    private static LruCache<Integer, Drawable> createIconCache() {final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);final int cacheSize = maxMemory / 8;return new LruCache<Integer, Drawable>(cacheSize) {protected int sizeOf(Integer key, Drawable drawable) {if (drawable instanceof BitmapDrawable) {//计算一个这个BitmapDrawable占用内存Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();return bitmap.getByteCount() / 1024;}return 1;//否则返回1,默认情况下返回的就是1}};}

上面的代码时候创建一个缓存Drawable 的图片数据的LruCache 对象,在方法sizeOf 里面判断,如果当前缓存的是BitmapDrawable 类型的图片数据,则返回的就是这个图片的内存大小,否则返回1.

很明显,当sizeOf方法返回1的时候,就表示当前的LruCache缓存的数据数量是指键值对数量.下面来看看LruCache中sizeOf方法默认是如何定义的:

protected int sizeOf(K key, V value) {return 1;}

默认情况下就是返回1的.所以如果你的 LruCache缓存数据如果时图片并且/或者对缓存的数据的内存大小有限制的时候,就需要重写这个方法sizeOf(K key, V value)

(2)这个数据就会被移动到队列的最前面(这样维护来一个访问数据的顺序):其实很快可以知道,hashmap中的LinkedHashMap似乎就可以实现这个功能,LruCache当然也就使用来LinkedHashMap,来看看LruCache的构造方法就清楚了:

    public LruCache(int maxSize) {//这里maxSize是指当前LruCache缓存数据最大量if (maxSize <= 0) {throw new IllegalArgumentException("maxSize <= 0");}this.maxSize = maxSize;this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//初始化一个LinkedHashMap,并且第三个参数是true,这表明这个LinkedHashMap是按照按访问顺序排序的.}

(3) 已经满了的缓存:每次使用 LruCache的时候:调用put(K key, V value)或者调用get(K key) 时候都会调用trimToSize(int maxSize)检测一下 LruCache是否已经满了.

(4)被清理掉:同上面一样,在调用trimToSize检测是否已经满来的时候,如果已经满来就会清理掉,看看源码:

    public void trimToSize(int maxSize) {while (true) {//一个死循环:直到当前LruCache没有满才会跳出这个循环K key;V value;synchronized (this) {if (size < 0 || (map.isEmpty() && size != 0)) {//异常错误检测:当前已经缓存的数据数量不能为负数,并且没有数据的时候其数据数量不能大于0throw new IllegalStateException(getClass().getName()+ ".sizeOf() is reporting inconsistent results!");}if (size <= maxSize) {//如果已经缓存的数据数量小于最大容量(没有满),则跳出循环break;}Map.Entry<K, V> toEvict = map.eldest();//通过LinkedHashMap来获取最不常用的一个键值对数据if (toEvict == null) {break;}key = toEvict.getKey();value = toEvict.getValue();map.remove(key);  //删除这个数据size -= <span style="color:#FF0000;">safeSizeOf</span>(key, value); //获取这个删除的键值对数据所占用的容量(有可能是1,也有可能是计算的图片占用的内存,也有可能是其他)evictionCount++;}<span style="color:#FF0000;">entryRemoved</span>(true, key, value, null); //回调用entryRemoved,一般用于通知客户端,这个键值对数据已经被清理出LruCache了}} 

上面的说明已经很清楚了,不过红色标识的两个方法需要下面来说说:

  • <span style="color:#FF0000;">safeSizeOf<span style="color:#000000;">来看看函数原型:</span></span>
        private int safeSizeOf(K key, V value) {int result = sizeOf(key, value);if (result < 0) {throw new IllegalStateException("Negative size: " + key + "=" + value);}return result;}

    看到了吗,其实就是调用了sizeOf这个方法.

  • entryRemoved 来看看函数原型:   protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} 是一个空的函数,其实这个方法时给子类重写的,如果你想要知道你的LruCache在悄悄删除数据的时候能够告知你的话,你就需要重写这个方法,比如:你创建一个存储bitmap的数据的LruCache,你可能想要在LruCache清理掉这个数据的时候,自己再调用这个bitmap的recycle()来是否资源的话,你就需要重写entryRemoved了.

到了这里,应该对LruCache比较了解,不过一个问题由此出来了,不知道大家有没有想到:既然LruCache会自己清理掉最不常用的键值对数据,可是最不常用也不代表就不会使用呀!如果,突然使用了已经被清理掉的键值对数据这么办!

举个例子:我的listview来显示100个菜单项,每一个菜单项来显示一个图片,每一个图片都有100k大小,我的LruCache最大容量只有1000k,这样一来当我滑动菜单的时候肯定LruCache会清除掉很多图片.那这么办呢?如果我能够知道已经清理掉的数据需要再次使用就可以了,那我可以重新创建.

为了解决这个问题,LruCache里面的方法get(K key)会去检测当前获取的数据是否为null,如果是null,则调用方法create来创建.默认情况下create这个方法直接返回 null;所以你自己的LruCache 是需要重写这个方法的.下面来看看get(K key)方法的源码:

    public final V get(K key) {//通过key来获取存储的数据if (key == null) {throw new NullPointerException("key == null");}V mapValue;synchronized (this) {mapValue = map.get(key);//其实是通过LinkedHashMap来获取的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);//如果当前<font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><span style="color:#FF0000;"><span style="color:#000000;"><span style="color:#FF0000;">LruCache</span></span></span></font></font></font></font></font></font>没有这个数据,则调用create创建if (createdValue == null) {return null;}synchronized (this) {createCount++;//更新自己创建的数量mapValue = map.put(key, createdValue);//将新创建的数据加入到LinkedHashMap里面if (mapValue != null) {// There was a conflict so undo that last putmap.put(key, mapValue);//如果冲突来再次尝试} else {size += safeSizeOf(key, createdValue);//更新当前<font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><span style="color:#FF0000;"><span style="color:#000000;"><span style="color:#FF0000;">LruCache</span></span></span></font></font></font></font></font></font> 已经存储数据的数量}}if (mapValue != null) {entryRemoved(false, key, createdValue, mapValue);//调用entryRemoved通知已经清理掉了一个数据return mapValue;} else {trimToSize(maxSize);//检测是否已经满了return createdValue;}}

说道到这里 ,我想已经说的很清楚了. LruCache 这个工具类其实比较简单的.除了上面我说的特点外,还有其他的一些不太常用的东西,下面来看看里面的几个源方法:

     public final V put(K key, V value) {//往<font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><span style="color:#FF0000;"><span style="color:#000000;"><span style="color:#FF0000;">LruCache</span></span></span></font></font></font></font></font></font></font></font></font></font>里面加入数据
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;//加入数据个数的更新
size += safeSizeOf(key, value);//<font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><font color="#FF0000"><font color="#000000"><span style="color:#FF0000;"><span style="color:#000000;"><span style="color:#FF0000;">LruCache</span></span></span></font></font></font></font></font></font></font></font></font></font> 存储数据的数量更新
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);检测是否已经满了
return previous;
}
    public final void evictAll() {//清理掉所有数据trimToSize(-1); // -1 will evict 0-sized elements}

LruCache的终极解析相关推荐

  1. LruCache的深入解析

    LruCache的深入解析 大家在学习图片多级缓存的时候,肯定都会接触过LruCache这个类,本片文章对LruCache进行一个独特的,深入的分析.主要分为以下两点 LinkedHashMap介绍 ...

  2. LruCache算法原理解析

    本来想写关于LruCache算法的原理解析文章,但是看到卡神的作品,写的太好了.所以这里我还是转载一下吧. 史上最详细LrcCache算法解析:https://blog.csdn.net/carson ...

  3. LruCache源码解析

    lrucache,最近最少使用缓存策略,源码其实很简单,没有多少行.下面我们分两个部分来解析: 第一部分:如何使用 /*** 存储的key类型* 存储的value类型* 设置最大存储容量* 计算每个存 ...

  4. LruCache 源码解析

    前言: 没看懂LruCache之前,我擦, LruCache这个类设计有啥意义? LinkedHashMap 完全可以代替.看懂之后,设计的真好. LruCache 概念描述 一般来说,缓存策略主要包 ...

  5. 【FFMPEG源码终极解析】void av_packet_unref(AVPacket *pkt)

    av_packet_unref 该接口使用了如下调用,该接口主要作用是清理AVPacket中的所有空间数据,清理完毕后进行初始化操作,并且将 data 与 size 置为0,方便下次调用. void ...

  6. 【FFMPEG源码终极解析】 avformat_open_input (一)

    avformat_open_input   打开媒体函数,先上全部源码.然后逐语句分析. int avformat_open_input(AVFormatContext **ps, const cha ...

  7. WAV文件格式终极解析

    最近想做一个播放使用DirectX播放PCM音频的程序,需要解析WAV文件,于是在网上搜索了一下,结果还是很多的,不过基本都是两篇文章的复本. 随后按照文章上所说对一个WAV文件做了一下解析,但结果发 ...

  8. 爬虫进阶-- 字体反爬终极解析

    爬取一些网站的信息时,偶尔会碰到这样一种情况:网页浏览显示是正常的,用python爬取下来是乱码,F12用开发者模式查看网页源代码也是乱码.这种一般是网站设置了字体反爬 什么是字体反爬? 字体反爬虫: ...

  9. 邻接表终极解析===和vector写法的区别

    参考:https://blog.csdn.net/deritt/article/details/50640997 先放写法,再看例子就懂了 首先我们需要定义一个数组 head[] int head[m ...

最新文章

  1. 技校毕业是什么学历_技校毕业是什么学历
  2. linux与unix时间戳互转
  3. svn update一直卡哪里_电脑开机一直停在LOGO那里这是为什么呢?
  4. android support Percent支持库开发
  5. java object转map_Java反序列化学习之CommonsCollections1
  6. opencv 常见细碎问题解决
  7. Linux 命令之 stat -- 显示文件的状态信息
  8. python 生成字符串_Python字符串生成器,按照特定的顺序
  9. ubuntu server 14.10 安装 nodejs
  10. phpStudy下载安装+配置站点+You don't have permission to access / on this server错误解决
  11. 容易造成单片机内存溢出的几个陷阱
  12. .net mysql 多线程_.net线程详解(转)
  13. 1053.互换最大最小数
  14. tomcat安装并设置开机启动
  15. Regularized least-squares classification(RLSC)
  16. Ubuntu 安装网易云音乐
  17. 单例模式详解(线程安全,饿汉,懒汉模式)
  18. SecureCRT 8.5下载安装破解
  19. Matlab学习笔记(一)--数值数据
  20. 让GIS三维可视化变得简单-地理坐标系统

热门文章

  1. vue项目 设置scrollTop不起作用(解决方法及原因)
  2. python制作辅助和易语言的区别_为什么多数外挂都用易语言?
  3. c++define的用法
  4. 类似火车头的采集器-免费任意数据采集器
  5. 2020-12-2 IDEA2020.2 提示Typo:In word ‘XXXX’ 解决方法
  6. mysql 循环之continue
  7. vue中怎么获取元素
  8. SQL常用脚本整理,建议收藏
  9. LWN:万维网之外的选择!
  10. 文件的下载(2)——解决下载文件名的乱码问题