Android中的LruCache的原理和使用

LruCache,虽然很多文章都把LRU翻译成“最近最少使用”缓存策略,但Android中的LruCache真的如此吗?
答案是No,它只做到了控制“最近使用”!

原理

数据结构

LruCache采用LinkedHashMap作为存储的数据结构,那么为什么是LinkedHashMap?

LinkedHashMap特性简介

  • LinkedHashMap基于HashMap,具有HashMap高效查找、自动扩容等特性
  • 在HashMap基础上,增加了一个双向链表存储K-V对、实现了自己的遍历器LinkedHashIterator,默认情况下可以做到根据数据插入顺序有序地遍历
  • 提供重载构造方法供外部控制accessOrder,以实现根据访问顺序有序地遍历

初始化

    public LruCache(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        this.maxSize = maxSize;        this.map = new LinkedHashMap(0, 0.75f, true);    }

LruCache的构造方法如上,可见LruCache初始化时就使用了上面提到的LinkedHashMap的第三点特性,即在数据结构层面实现了“最近使用”。

存储

当调用put方法添加/设置存储内容时,LruCache依次做了这么几件事:

  1. 判空,即不允许key/value为null
  2. 总容量size增加上计算传入的K-V大小
  3. 将传入的K-V存入LinkedHashMap
  4. 如过LinkedHashMap中已存在相同K,总容量size减去替换掉的K-VOld大小
  5. 通知VOld被替换(子类实现entryRemoved以监听)
  6. 比较总容量size和最大容量maxSize,若大于maxSize则循环移除LinkedHashMap头结点(即最久未被访问的结点)直至size小于等于maxSize

获取

当调用get方法获取存储内容时,LruCache依次做了这些事:

  1. 判空
  2. 从LinkedHashMap中取出与K对应的V值并返回。如果子类未实现create方法以达到当缓存未命中时创建并存入新V的话,返回null,get流程结束。
  3. 通过create创建VNew,并尝试把VNew存储到LinkedHashMap中,流程类似存储过程,不同之处在于当K冲突时,会舍弃掉新创建的VNew。不要奇怪为什么明明上面通过K取V的时候没取到,这里却会K冲突,因为LruCache为了性能考虑(防止子类自定义的create方法耗时过长影响get方法执行性能),只对从LinkedHashMap中取值的过程做了同步处理,这样在多线程的情况下就可能出现A线程在create的时候,B线程已经将K-VB存入了map。
  4. 返回上面创建的VNew或者VB

使用

用LruCache实现一个简单的图片缓存

    class LruImageCache extends LruCache {        private static final String TAG = "LruImageCache";        private static final int DEFAULT_CACHE_SIZE = 20 * 1024 * 1024;        public LruImageCache() {            this(DEFAULT_CACHE_SIZE);        }        public LruImageCache(int maxSize) {            super(maxSize);        }        @Override        protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {            Log.d(TAG, "cache removed: " + key);        }        @Override        protected Bitmap create(String key) {            // 从本地、网络获取图片            return loadImageFromIO(key);        }                @Override        protected int sizeOf(String key, Bitmap value) {            return value.getAllocationByteCount();        }    }
//使用Bitmap b = LruImageCache.get("http://image-path");

更多相关文章

  1. Android Studio 中方法数65536 或超过64K的解决方案
  2. Android SDK使用迅雷下载方法
  3. Android应用程序启动时发生AndroidRuntime : ClassNotFoundExcep
  4. Android的Activity的launchMode与onActivityResult方法的关系
  5. Android DrawerLayout和NavigationView 的使用方法
  6. Android编译本地C++程序方法
  7. Android开发者网站打不开的解决方法
  8. android sdk manager 无法更新解决方法
  9. Android下app生成coredump方法

随机推荐

  1. 通过备份记录获取数据库的增长情况
  2. sqlserver 2000 远程连接 服务器的解决方
  3. Sql Server 字符串聚合函数
  4. insert into tbl() select * from tb2中
  5. sql 去零函数 datagridview中数字类型常
  6. table 行转列的sql详解
  7. 三步堵死 SQL Server注入漏洞
  8. 常用SQL功能语句
  9. 创建动态MSSQL数据库表
  10. SQL Server 版本变更检查 警告