文章目录
LruCache
概述
LruCache是Android 3.1所提供的一个缓存类,所以在Android中可以直接使用LruCache实现内存缓存。
主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
简单使用
int maxMemory = (int) (Runtime.getRuntime().totalMemory()/1024);
int cacheSize = maxMemory/8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
}
};
- 设置LruCache缓存大小
- 重写sizeOf方法计算每张图片大小
原理
维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。
这个队列的维护基于LinkedHashMap。
LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。
https://www.jianshu.com/p/b49a111147ee
DiskLruCache
概述
DiskLruCache 的分析分为两部分:分别是日志,读、写缓存。
DiskLruCache 的日志就是一个操作记录。例如,删除一条缓存条目,就会在日志文件中记录一条 REMOVE 记录;新建一条缓存,缓存文件刚建立时会增加一条 DIRTY 记录,缓存写入成功后再增加一条 CLEAN 记录;缓存被读取,会增加 READ 记录。日志文件的格式如下:
记录日志是为了在 DiskLruCache 初始化时建立起缓存的 LRU 结构。DiskLruCache 内部使用了 LinkHashMap 来存储所有的缓存项,在初始化的时候,DiskLruCache 会读日志文件上的记录,根据该日志文件的记录将所有历史操作“重做”一遍:在执行历史操作时,会根据日志记录对缓存项进行添加和删除操作。为什么这样能保证缓存项是按 LRU 的顺序排序的呢?因为构造 LinkedHashMap 的时候选择使用 Access Order(访问顺序)来保持元素的顺序,因此只需遍历日志根据日志记录向 LinkedHashMap 中放入缓存项(Entry)自然而然地就保持了 LRU 的顺序。
http://liwenkun.me/2017/08/29/glide-disk-cache-strategy/
Glide缓存概述
资源分类
Glide的缓存图片资源分为两类
- 原始图片(Source)——原始图片大小
- 转换后的图片(Result)——经过大小处理过的图片
使用Glide加载图片时,Glide默认会根据View对图片进行压缩转换。
缓存设计
- Glide缓存设计为二级缓存:内存缓存和硬盘缓存
- 缓存读取顺序:内存缓存 --> 磁盘缓存 --> 网络
- 内存缓存:防止应用重复将图片数据读取内存当中——只缓存转换过后的图片
- 硬盘缓存:防止应用重复从网络或其他地方重复下载和读取数据——可缓存原始图片、转换过后的图片
Glide内存缓存实现基于LruCache 算法 + 弱引用机制
- LruCache算法原理:将最近使用的对象,用强引用的方式 存储在LinkedHashMap中 ;当缓存满时 ,将最近最少使用的对象从内存中移除
- 弱引用:弱引用的对象具备更短生命周期,因为 当JVM进行垃圾回收时,一旦发现弱引用对象,都会进行回收(无论内存充足否)
磁盘缓存使用Glide 自定义的DiskLruCache算法
- 该算法基于 Lru 算法中的DiskLruCache算法,具体应用在磁盘缓存的需求场景中
- 该算法被封装到Glide自定义的工具类中(该工具类基于Android 提供的DiskLruCache工具类)
Glide 缓存源码分析
1、生成key
Glide实现内存、磁盘缓存是根据图片缓存key进行唯一标识
生成缓存 Key 的代码发生在Engine类的 load()中
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
// 获得了一个id字符串,即需加载图片的唯一标识
// 如,若图片的来源是网络,那么该id = 这张图片的url地址
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());
// Glide的缓存Key生成规则复杂:根据10多个参数生成
// 将该id 和 signature、width、height等10个参数一起传入到缓存Key的工厂方法里,最终创建出一个EngineKey对象
// 创建原理:通过重写equals() 和 hashCode(),保证只有传入EngineKey的所有参数都相同情况下才认为是同一个EngineKey对象
// 该EngineKey 即Glide中图片的缓存Key
...
概括一下
- 获得了一个id字符串,若图片的来源是网络,那么该id = 这张图片的url地址
- Glide的缓存Key生成规则:根据10多个参数生成,将该id 和 signature、width、height等10个参数一起传入到缓存Key的工厂方法里,最终创建出一个EngineKey对象
2、创建缓存对象LruResourceCache
LruResourceCache对象是在创建 Glide 对象时创建的
public class GlideBuilder {
...
Glide createGlide() {
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
// 创建一个LruResourceCache对象 并 赋值到memoryCache对象
// 该LruResourceCache对象 = Glide实现内存缓存的LruCache对象
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
}
这里没什么多说的。
3、获取内存缓存中的图片
读取内存缓存代码实在Engine类的load()方法中
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder