图片缓存策略
来源:互联网 发布:淘宝店家如何开通花呗 编辑:程序博客网 时间:2024/06/05 03:26
众所周知,基于客户端app的开发中都会涉及到大量的图片,包括在线或者本地内置的,而对于在线图片的读取如果都实施从网络上读,会造成大量流量的浪费并且交互非常糟糕。所以对于已经读取过的在线图片,需要在本地有一些缓存以便快速读取展现给用户,而本地缓存主要策略包括:
内存缓存+sd卡缓存双缓存机制:
内存缓存策略LruCache:Least Recently Used最近最少使用算法即会淘汰最近最少使用的数据,可以看看源码:
public class LruCache<K, V> { 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; private int createCount; 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); } ...}
可以看到LruCache的构造函数可以指定缓存的最大容量,并初始化了一个LinkedHashMap,也就是LruCache主要依赖LinkedHashMap实现的核心算法。
LruCache关键方法有:从缓存取数据get、向缓存存数据put、移除最近最少使用的数据trimToSize方法,下面依次看这三个方法
/** * 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); 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; } }
1、从map取数据 map存在对应的value则直接返回value
2、从map取数据 map不存在则去create一个对应的数据并put到map中,重新计算大小之后调用trimToSize方法,删除访问次数最少的元素
/** * 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) { 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; }
将键值对放入map,重新计算大小之后调用trimToSize方法,删除访问次数最少的元素
/** * 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) { break; } Map.Entry<K, V> toEvict = map.eldest(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }
1、当前size小于maxSize 不做任何操作
2、当前size大于等于maxSize 取出最近最少使用的数据移除并调整size
sd卡缓存策略DiskLruCache:
源码参考:
https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java
源码详解:
http://blog.csdn.net/lmj623565791/article/details/47251585
DiskLruCache缓存框架中有一个关键的文件journal文件,这个文件会存储所有的读取操作记录
journal文件格式
libcore.io.DiskLruCache111DIRTY c3bac86f2e7a291a1a200b853835b664CLEAN c3bac86f2e7a291a1a200b853835b664 4698READ c3bac86f2e7a291a1a200b853835b664DIRTY c59f9eec4b616dc6682c7fa8bd1e061fCLEAN c59f9eec4b616dc6682c7fa8bd1e061f 4698READ c59f9eec4b616dc6682c7fa8bd1e061fDIRTY be8bdac81c12a08e15988555d85dfd2bCLEAN be8bdac81c12a08e15988555d85dfd2b 99READ be8bdac81c12a08e15988555d85dfd2bDIRTY 536788f4dbdffeecfbb8f350a941eea3REMOVE 536788f4dbdffeecfbb8f350a941eea3
- 第一行固定字符串libcore.io.DiskLruCache
- 第二行DiskLruCache的版本号,源码中为常量1
- 第三行为你的app的版本号
- 第四行指每个key对应几个文件,一般为1
- 第五行,空行
以上5行可以称为该文件的文件头,DiskLruCache初始化的时候,如果该文件存在需要校验该文件头。
接下来的行,可以认为是操作记录。
DIRTY 表示一个entry正在被写入(其实就是把文件的OutputStream交给你了)。那么写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。
REMOVE除了上述的情况呢,当你自己手动调用remove(key)方法的时候也会写入一条REMOVE记录。
READ就是说明有一次读取的记录。
每个CLEAN的后面还记录了文件的长度,注意可能会一个key对应多个文件,那么就会有多个数字(参照文件头第四行)。
从这里看出,只有CLEAN且没有REMOVE的记录,才是真正可用的Cache Entry记录。
- android 图片缓存策略
- android图片缓存策略
- 图片缓存策略
- Android图片缓存策略
- android 网络图片缓存策略
- Android图片缓存策略 & OOM
- 图片缓存实现策略分析
- iOS中图片缓存策略
- 【缓存图片】网络图片本地存储策略
- Android网络图片三级缓存策略
- Android中图片的三级缓存策略
- Android中图片的三级缓存策略
- 图片的三级缓存cache策略
- 图片加载UIL的Disk缓存策略
- 图片的二次采样三级缓存策略
- 图片加载库之缓存策略和加载策略
- iOS网络加载图片缓存策略之ASIDownloadCache缓存优化
- IOS开发网络加载图片缓存策略之──ASIDownloadCache缓存策略
- Jquery(八) 属性过滤
- wampserver 3.0.X以上版本怎么切换启用服务器在线状态
- UVA 11426 GCD
- framework层和native层实现联网控制(iptable方式)
- C#
- 图片缓存策略
- ES6新特性之迭代器与for-of循环
- js获取、设置元素css属性值
- 使用spring注解@Controller @Service @Repository简化配置
- 添加第三方jar包到nexus库(by -louis)
- python实现12306车票查询
- Spring IOC AOP学习
- SpringMVC 页面传Date类型值提示Could not instantiate bean class [java.util.Date]: Constructor threw exception
- ROC AUC指标详解