android LruCache 原理 以及 源代码解析
来源:互联网 发布:餐饮进销存软件免费版 编辑:程序博客网 时间:2024/05/22 13:33
使用LRU(Least recently used,最近最少使用)算法缓存技术能大大提升程序性能。
原理:
1. 新数据插入到链表头部;
2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3. 当缓存内容超过指定大小的时候,将链表尾部的数据丢弃。
了解原理后,我们看下android中LruCache 的源代码实现。
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); }
构造函数中的参数是指定缓存的总大小 提一点 默认是按个数来实现 如果你想按其他方式来算,需要重写sizeOf方法,实现你自己的算法。
然后我们看put方法
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; }
通过safeSizeOf方法算出put进来的大小,put完后调用trimToSize检查是否超过指定大小
public void trimToSize(int 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!"); } if (size <= maxSize || map.isEmpty()) { break; } Map.Entry<K, V> toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }这个方法比较简单,一个while循环,如果超过指定大小,不断得丢掉链表尾巴上的对象。
然后还有一个重要的get方法。
public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) { 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. */ V createdValue = create(key); if (createdValue == null) { return null; } synchronized (this) { createCount++; mapValue = map.put(key, createdValue); if (mapValue != null) { // There was a conflict so undo that last put map.put(key, mapValue); } else { size += safeSizeOf(key, createdValue); } } if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { trimToSize(maxSize); return createdValue; } }
这里逻辑也比较简单,先从map取 取到了就返回 如果没取到 调用create方法创建,创建完后就跟put逻辑差不多。
这个create方法可以算第二层缓存逻辑,比如去本地文件加载进来。
以上,代码实现都比较简单,或者说过于简单了,有一段时间我就纳闷,这代码里没实现 LRU原理的其中一点(命中数据提到表头)这个逻辑啊,
/** * Returns the value for {@code key} if it exists in the cache or can be * created by {@code #create}. If a value was returned, it is moved to the * head of the queue. This returns null if a value is not cached and cannot * be created. */一看注释,注释也都表明有实现这个逻辑。然后找啊找,终于明白。
在lruCache的构造函数中
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);第三个参数是true,这个参数表明这个linkedMap的排列规则
跑到LinkedHashMap中查看get方法实现如下,
public V get(Object key) { /* * This method is overridden to eliminate the need for a polymorphic * invocation in superclass at the expense of code duplication. */ if (key == null) { HashMapEntry<K, V> e = entryForNullKey; if (e == null) return null; if (accessOrder) makeTail((LinkedEntry<K, V>) e); return e.value; } int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { if (accessOrder) makeTail((LinkedEntry<K, V>) e); return e.value; } } return null;}
注意到标红的部分,原来是在这儿实现的
在看put方法,put方法是hashmap中实现的:
public V put(K key, V value) { if (key == null) { return putValueForNullKey(value); } int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // No entry for (non-null) key is present; create one modCount++; if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } addNewEntry(key, value, hash, index); return null;}
然后linkedHashmap重写了preModify方法void preModify(HashMapEntry<K, V> e) { if (accessOrder) { makeTail((LinkedEntry<K, V>) e); } }
===========================华丽的分割线===============================
LruCache能解决内存缓存的缓存优化,但同样,我们的文件缓存(硬盘缓存)也同样需要如此,否则用户的硬盘空间被我们的程序越用越小要骂娘了0.0
针对这个Google又提供了一套硬盘缓存的解决方案:DiskLruCache(非Google官方编写,但获得官方认证)。只可惜,Android Doc中并没有对DiskLruCache的用法给出详细的说明 http://www.mobile-open.com/2014/3104.html 这个连接介绍的比较好,可以学习。
0 0
- android LruCache 原理 以及 源代码解析
- Android LruCache 实现原理解析
- android源代码之LruCache
- Android LruCache技术原理
- android LRUCache解析
- android LruCache源码解析
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- jQuery工作原理解析以及源代码示例
- Linux软硬连接
- 安卓ViewPager--OnPageChangeListener
- 工厂方法模式的结构
- Silicon Labs EFM8 利用UID唯一标识产品
- MapReduce性能优化_3. 诊断 Reduce 端性能瓶颈
- android LruCache 原理 以及 源代码解析
- Android Touch事件传递机制通俗讲解
- 从零学Android(十三)、Android中的数据存储方式简介
- NGUI制作弹窗
- 最近开发的心得1
- Java学习笔记-适配器用法
- html name标签命名规范
- 安卓Viewpager--页面切换控制
- JavaScript学习(5.5.1):跳转之标签