Android 源码解析-LruCache 缓存工具类
2017-10-25 16:37
295 查看
关于Android的三级缓存,其中主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LruCache算法,今天我们就从使用到源码解析,来彻底理解Android中的缓存机制。
第一步 设置缓存大小,一般为当前进程可用容量的1/8。
第二步 重写sizeOf方法,计算出要缓存的每张图片的大小。
LinkedHashMap中双向链表的结构是访问顺序实现,accessOrder = ture
这样我们就完成了对LruCache的使用,现在我们发现这个缓存类也没那么复杂,用法也十分简单,正是因为简单,所以我们可以很方便的对其进行扩展。当然了,这仅仅是做了内存缓存,熟悉缓存机制的朋友一定会知道磁盘缓存和内存缓存二者的关系,有关磁盘缓存的问题我将在以后的文章中进行讲述。
1、LruCache的介绍
LruCache是Android API 12以后提供的一个缓存工具类,采用了最近最少使用算法。它把最近使用的对象用“强引用”存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前就从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。2、LruCache的使用
以图片缓存为例private LruCache<String, Bitmap> mMemoryCache; public MemoryCache() { final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 4; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } }; }
第一步 设置缓存大小,一般为当前进程可用容量的1/8。
第二步 重写sizeOf方法,计算出要缓存的每张图片的大小。
3、LruCache 的实现原理
LruCache 使用LinkedHashMap维护一个列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。如下图LinkedHashMap中双向链表的结构是访问顺序实现,accessOrder = ture
LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>(10, 0.75f, true); linkedHashMap.put(0, 0); linkedHashMap.put(1, 1); linkedHashMap.put(2, 2); linkedHashMap.put(3, 3); linkedHashMap.put(4, 4); linkedHashMap.get(1); linkedHashMap.get(2); linkedHashMap.put(5, 5); for (Map.Entry<Integer, Integer> entry : linkedHashMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); }
4、LruCache 的源码解析
构造方法与属性变量
private final LinkedHashMap<K, V> map; /** Size of this cache in units. Not necessarily the number of elements. */ private int size; //已存储的大小 private int maxSize; //最大的存储空间 private int putCount; //put放大调用的次数 private int createCount; //create 的次数 private int evictionCount; //回首的次数 private int hitCount; //命中的次数 private int missCount; //丢失的次数 /** * @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. */ 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); }
put 方法
/** * Caches {@code value} for {@code key}. The value is moved to the head of * the queue. * * @return the previous value mapped by {@code key}. */ public final V put(K key, V value) { if (key == null || value == null) { //key 和 value 不能为null throw new NullPointerException("key == null || value == null"); } V previous; synchronized (this) { putCount++; //put 后值加一 size += safeSizeOf(key, value); //增加当前缓存的大小 previous = map.put(key, value); //加入缓存对象 if (previous != null) { // 如果cache 中存在对象则恢复到之前的大小 size -= safeSizeOf(key, previous); } } if (previous != null) { //空算法 entryRemoved(false, key, previous, value); } trimToSize(maxSize); //调整缓存大小 return previous; }
trimToSize(int maxSize)
/** * Remove the eldest entries until the total of remaining entries is at or * below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */ 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) { //如果当前的size 小于最大size 跳出循环 break; } Map.Entry<K, V> toEvict = map.eldest(); //取队尾的元素,近期最少访问的元素 if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); //删除队尾的元素并更新当前size 的大小 size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }
get(K key)
/** * 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. */ public final V get(K 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++; }
这样我们就完成了对LruCache的使用,现在我们发现这个缓存类也没那么复杂,用法也十分简单,正是因为简单,所以我们可以很方便的对其进行扩展。当然了,这仅仅是做了内存缓存,熟悉缓存机制的朋友一定会知道磁盘缓存和内存缓存二者的关系,有关磁盘缓存的问题我将在以后的文章中进行讲述。
相关文章推荐
- Android内存缓存管理LruCache源码解析与示例
- android LruCache内存缓存源码解析
- android lru缓存 辅助类LruCache源码解析
- Android内存缓存LruCache源码解析
- Android DiskLruCache磁盘缓存完全解析及使用
- Android DiskLruCache 源码解析 硬盘缓存的绝佳方案
- Android LruCache 缓存 类 源码 注解 分析
- Android缓存源码分析(DiskLruCache,LruCache)
- Android-UIL图片缓存框架 源码解析
- Android源码解析——LruCache
- 【DiskLruCache完全解析】Android AdapterView图片硬盘缓存的最佳方案
- Android进阶图片处理之DiskLruCache解析 硬盘缓存方案
- Android DiskLruCache缓存完全解析
- Android DiskLruCache缓存完全解析
- android开发-LRU缓存源码解析
- 【LruCache完全解析】Android高效加载大图、多图解决方案,有效避免程序OOM (AdapterView使用LruCache图片缓存)
- Android源码解析——LruCache
- Android源码解析——LruCache
- Android DiskLruCache 源码解析 硬盘缓存的绝佳方案
- android LruCache源码解析