Glide缓存机制源码浅析
内容概览
- 流行的图片加载框架
- 常见的缓存策略
- LruCache
- Glide源码分析缓存机制
核心内容
- 目前流行的专注于图片加载的框架:Picasso\Fresco\Volley(Imageloader)\Glide,无论什么框架大体的思想都是类似的,首次读取从网络加载存到本地(一级、二级、三级、、、缓存),再次读取从本地读取(一级、二级、三级…缓存),最终两者都可以合并成第二种情况,用一种逻辑处理即可,这里主要记录Glide cache包下的缓存机制解读
- 常见的缓存策略和LrcCache相关知识可以查阅我的另一篇 Volley网络请求封装之LruCache源码分析来补充知识,这里将不再做赘述,但是还是要简单的提一句Volley的LruCache和Glide的LruCache类并不是一模一样,对外的方法稍有改动,但是内部机制还是一样的都是利用LinkedHashMap;
- Glide的缓存主要分为这几种处理方式:Wereference对象、MemeryCache、DiskCache、其中DiskCache又分为Internal和Extrenal,简单了解之后,我们就开始正式的分析吧。
源码浅析
目录结构
结构分析
以图中LruResourceCache为分界线上面的是关于本地磁盘的存储处理(非内存),包括LruResourceCache在内的下面的类是关于内存存储方式的处理。- 我们对比的来看:首先无论内存还是外存都提供了处理的API接口:DiskCache和MemoryCache这意味着,他们对应的Adapter使他们的类型是,只要创建对象你就可以选择实现需要重写的缓存方法,这种适配器的方式是Google大神常用的讨论,在另一篇博客DrawerLayout的简单使用的DrawerLayout的源码中也有类似的实现哦,主要是为了是代码更简洁提高可读性,你不想用系统提供的也可以自己玩但是我劝你还是算了吧不到迫不得已不要重复造轮子,Glide已经很强大了哦。
- LruResourceCache是内存方面的主要实现类,看一下类的声明相比我什么也不用说你也就明白是怎么回事了:
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {...}
同时这个类里面要特别说明一个实现,它监听了被删除Item并对其进行了相应的处理,主要实现是通过回调实现的,具体请看下面:
private ResourceRemovedListener listener;
/**
* Constructor for LruResourceCache.
*
* @param size The maximum size in bytes the in memory cache can use.
*/
public LruResourceCache(int size) {
super(size);
}
@Override
public void setResourceRemovedListener(ResourceRemovedListener listener) {
this.listener = listener;
}
@Override
protected void onItemEvicted(Key key, Resource<?> item) {
if (listener != null) {
listener.onResourceRemoved(item);
}
}
其实我追溯到Engine类里面发现它只是简单的做了删除操作,这与我上面提到的Volley那篇博客中我的处理方式稍有不同,我是讲删除的内容存到了外部存储其中,不过Google大神这样做可能想要将解耦做到极致吧,在Glide中也确实是这样实现的,DiskCache和MemoryCache没有任何感情纠纷的成分。内存存储的需要了解的地方相比之前的图片缓存框架到这基本就结束了,来看看外部存储器的吧。
在DiskCache这边DiskLruCacheWrapper实现了DiskCache处理的核心精髓,内部提供的get、put、delete方法都是司空见惯的方法,没什么说的,与此同时他结合DiskLruCacheFactory来实现了DiskCache的创建与初始化工作,而在DiskLruCacheWrapper中正对选择磁盘的外存还是内存方式做了处理,如果不存在直接返回null,与大多数存储方式套路是一样的,因为这是时下最流行的性感;
激活缓存机制:我们在使用Glide的时候会向下面这样使用:
Glide.with(myViewHolder.imageIv.getContext()).load(zhuangBiImage.image_url).into(myViewHolder.imageIv);
- 我想你已经猜出来了,是load激活了缓存机制,在其内部的核心方法是loadGeneric做了处理,然后你还会注意到一个方法GenericRequestBuilder#obtainRequest在开始请求是无论什么情况都会调用该方法,当你点进这个方法是你发现世界开始变得明媚了,就在这个方法里Glide做了缓存机制的处理方式,请查看GenericRequest#init方法里面有你想要的答案:
// We allow null models by just setting an error drawable. Null models will always have empty providers, we
// simply skip our sanity checks in that unusual case.
if (model != null) {
check("ModelLoader", loadProvider.getModelLoader(), "try .using(ModelLoader)");
check("Transcoder", loadProvider.getTranscoder(), "try .as*(Class).transcode(ResourceTranscoder)");
check("Transformation", transformation, "try .transform(UnitTransformation.get())");
if (diskCacheStrategy.cacheSource()) {
check("SourceEncoder", loadProvider.getSourceEncoder(),
"try .sourceEncoder(Encoder) or .diskCacheStrategy(NONE/RESULT)");
} else {
check("SourceDecoder", loadProvider.getSourceDecoder(),
"try .decoder/.imageDecoder/.videoDecoder(ResourceDecoder) or .diskCacheStrategy(ALL/SOURCE)");
}
if (diskCacheStrategy.cacheSource() || diskCacheStrategy.cacheResult()) {
// TODO if(resourceClass.isAssignableFrom(InputStream.class) it is possible to wrap sourceDecoder
// and use it instead of cacheDecoder: new FileToStreamDecoder<Z>(sourceDecoder)
// in that case this shouldn't throw
check("CacheDecoder", loadProvider.getCacheDecoder(),
"try .cacheDecoder(ResouceDecoder) or .diskCacheStrategy(NONE)");
}
if (diskCacheStrategy.cacheResult()) {
check("Encoder", loadProvider.getEncoder(),
"try .encode(ResourceEncoder) or .diskCacheStrategy(NONE/SOURCE)");
}
}
- 时间紧迫,写的比较仓促,也因为套路都一样,所以这里只是将大体流程和核心思想记录下来。。。