Glide
,该功能很是强大 Android
图片加载开源框架 相信你们并不陌生 算法
正因为他的功能强大,因此它的源码很是复杂,这致使不少人望而却步缓存
本人尝试将 Glide
的功能进行分解,并单独针对每一个功能进行源码分析,从而下降Glide
源码的复杂度。安全
接下来,我将推出一系列关于
Glide
的功能源码分析,有兴趣能够继续关注bash
Glide
的图片缓存功能进行流程 & 源码分析 ,但愿大家会喜欢。因为文章较长,但愿读者先收藏 & 预留足够时间进行查看。服务器
Glide
须要缓存的 图片资源 分为两类:微信
Source
) :即图片源的图片初始大小 & 分辨率Result
) :通过 尺寸缩放 和 大小压缩等处理后的图片当使用
Glide
加载图片时,Glide默认 根据View
视图对图片进行压缩 & 转换,而不显示原始图(这也是Glide
加载速度高于Picasso
的缘由)网络
Glide
的缓存功能设计成 二级缓存:内存缓存 & 硬盘缓存并非三级缓存,由于 从网络加载 不属于缓存app
- 内存缓存 默认开启
Glide
中,内存缓存 & 磁盘缓存相互不影响,独立配置
只 缓存转换事后的图片框架
可缓存原始图片 & 缓存转换事后的图片,用户自行设置ide
Glide
的缓存机制使得 Glide
具有很是好的图片缓存效果,从而使得具有较高的图片加载效率。
如,在
RecyclerView
上下滑动,而RecyclerView
中只要是Glide
加载过的图片,均可以直接从内存中读取 & 展现,从而不须要重复从 网络或硬盘上读取,提升图片加载效率。
Glide
的缓存功能分为:内存缓存 & 磁盘缓存只 缓存转换事后的图片,而并不是原始图片
Glide
自动开启 内存缓存// 默认开启内存缓存,用户不须要做任何设置
Glide.with(this)
.load(url)
.into(imageView);
// 可经过 API 禁用 内存缓存功能
Glide.with(this)
.load(url)
.skipMemoryCache(true) // 禁用 内存缓存
.into(imageView);
复制代码
Glide
的内存缓存实现是基于:LruCache
算法(Least Recently Used
) & 弱引用机制
LruCache
算法原理:将 最近使用的对象 用强引用的方式 存储在LinkedHashMap
中 ;当缓存满时 ,将最近最少使用的对象从内存中移除- 弱引用:弱引用的对象具有更短生命周期,由于 **当
JVM
进行垃圾回收时,一旦发现弱引用对象,都会进行回收(不管内存充足否)
可缓存原始图片 & 缓存转换事后的图片,用户自行设置
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
// 缓存参数说明
// DiskCacheStrategy.NONE:不缓存任何图片,即禁用磁盘缓存
// DiskCacheStrategy.ALL :缓存原始图片 & 转换后的图片
// DiskCacheStrategy.SOURCE:只缓存原始图片(原来的全分辨率的图像,即不缓存转换后的图片)
// DiskCacheStrategy.RESULT:(默认)只缓存转换后的图片(即最终的图像:下降分辨率后 / 或者转换后 ,不缓存原始图片
复制代码
Glide
自定义的DiskLruCache
算法
- 该算法基于
Lru
算法中的DiskLruCache
算法,具体应用在磁盘缓存的需求场景中- 该算法被封装到
Glide
自定义的工具类中(该工具类基于Android
提供的DiskLruCache
工具类
Glide
整个缓存流程 从 加载图片请求 开始,其中过程 有本文最关注的 内存缓存的读取 & 写入、磁盘缓存的读取 & 写入下面,我将根据 Glide
缓存流程中的每一个步骤 进行源码分析。
Glide
实现内存 & 磁盘缓存 是根据 图片的缓存Key 进行惟一标识即根据 图片的缓存Key 去缓存区找 对应的缓存图片
Key
的代码发生在Engine
类的 load()
中#该代码在上一篇文章当中已分析过,只是当时忽略了缓存相关的内容,如今仅贴出缓存相关的代码
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
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
...
}
复制代码
至此,Glide
的图片缓存 Key
生成完毕。
LruResourceCache
LruResourceCache
对象是在建立 Glide
对象时建立的
#而 建立 Glide
对象则是在上篇文章 讲解 Glide
图片加载功能时 第2步load()
中loadGeneric()
建立 ModelLoader
对象时建立的
请看源码分析
<-- 第2步load()中的loadGeneric()-->
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
...
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
// 建立第1个ModelLoader对象;做用:加载图片
// Glide会根据load()方法传入不一样类型参数,获得不一样的ModelLoader对象
// 此处传入参数是String.class,所以获得的是StreamStringLoader对象(实现了ModelLoader接口)
// Glide.buildStreamModelLoader()分析 ->>分析1
<--分析1:Glide.buildStreamModelLoader() -->
public class Glide {
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
Context context) {
if (modelClass == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to load null model, setting placeholder only");
}
return null;
}
return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
// 建立ModelLoader对象时,调用Glide.get() 建立Glide对象-->分析2
}
<--分析2:Glide.get() -->
// 做用:采用单例模式建立Glide对象
public static Glide get(Context context) {
// 实现单例功能
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
// 经过建造者模式建立Glide对象 ->>分析3
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
}
<--分析3:builder.createGlide() -->
// 做用:建立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);
}
}
复制代码
至此,建立好了缓存对象LruResourceCache
Glide
在图片加载前就会从 内存缓存 中获取缓存图片Engine
类的load()
中即上面讲解的生成缓存
Key
的地方
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
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(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
// 上面讲解的生成图片缓存Key
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
// 调用loadFromCache()获取内存缓存中的缓存图片
if (cached != null) {
cb.onResourceReady(cached);
}
// 若获取到,就直接调用cb.onResourceReady()进行回调
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
}
// 若没获取到,就继续调用loadFromActiveResources()获取缓存图片
// 获取到也直接回调
// 若上述两个方法都没有获取到缓存图片,就开启一个新的线程准备加载图片
// 即从上文提到的 Glide最基础功能:图片加载
EngineJob current = jobs.get(key);
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
return new LoadStatus(cb, engineJob);
}
...
}
复制代码
即:
Glide
将 内存缓存 划分为两块:一块使用了LruCache
算法 机制;另外一块使用了弱引用 机制loadFromCache()
:从 使用了 LruCache
算法机制的内存缓存获取 缓存loadFromActiveResources()
:从 使用了 弱引用机制的内存缓存获取 缓存源码分析以下:
// 这2个方法属于 Engine 类
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
...
<-- 方法1:loadFromCache() -->
// 原理:使用了 LruCache算法
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
// 若isMemoryCacheable = false就返回null,即内存缓存被禁用
// 即 内存缓存是否禁用的API skipMemoryCache() - 请回看内存缓存的具体使用
// 若设置skipMemoryCache(true),此处的isMemoryCacheable就等于false,最终返回Null,表示内存缓存已被禁用
}
EngineResource<?> cached = getEngineResourceFromCache(key);
// 获取图片缓存 ->>分析4
// 从分析4回来看这里:
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
// 将获取到的缓存图片存储到activeResources当中
// activeResources = 一个弱引用的HashMap:用于缓存正在使用中的图片
// 好处:保护这些图片不会被LruCache算法回收掉。 ->>方法2
}
return cached;
}
<<- 分析4:getEngineResourceFromCache() ->>
// 做用:获取图片缓存
// 具体过程:根据缓存Key 从cache中 取值
// 注:此处的cache对象 = 在构建Glide对象时建立的LruResourceCache对象,即说明使用的是LruCache算法
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
// 当从LruResourceCache中获取到缓存图片后,会将它从缓存中移除->>回到方法1原处
final EngineResource result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource) cached;
} else {
result = new EngineResource(cached, true /*isCacheable*/);
}
return result;
}
<-- 方法2:loadFromActiveResources() -->
// 原理:使用了 弱引用机制
// 具体过程:当在方法1中没法获取内存缓存中的缓存图片时,就会从activeResources中取值
// activeResources = 一个弱引用的HashMap:用于缓存正在使用中的图片
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = null;
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}
...
}
复制代码
若上述两个方法都没获取到缓存图片时(即内存缓存里没有该图片的缓存),就开启新线程加载图片。
若没法从 内存缓存 里 得到缓存的图片,Glide
就会开启 加载图片的线程
但在该线程开启后,Glide
并不会立刻去网络 加载图片,而是采起采用Glide
的第2级缓存:磁盘缓存 去获取缓存图片
从 上篇文章:Android:这是一份全面 & 详细的图片加载库Glide源码分析中,在第3步 into()
中开启图片线程 run()
里的 decode()
开始(上文的分析13)
private Resource<?> decode() throws Exception {
// 在执行 加载图片 线程时(即加载图片时),分两种状况:
// 状况1:从磁盘缓存当中读取图片(默认状况下Glide会优先从缓存当中读取,没有才会去网络源读取图片)
// 状况2:不从磁盘缓存中读取图片
// 状况1:从磁盘缓存中读取缓存图片
if (isDecodingFromCache()) {
// 取决于在使用API时是否开启,若采用DiskCacheStrategy.NONE,即不缓存任何图片,即禁用磁盘缓存
return decodeFromCache();
// 读取磁盘缓存的入口就是这里,此处主要讲解 ->>直接看步骤4的分析9
} else {
// 状况2:不从磁盘缓存中读取图片
// 即上文讨论的从网络读取图片,此处不做过多描述
return decodeFromSource();
}
}
复制代码
若没法从 内存缓存 里 得到缓存的图片,Glide
就会采用第2级缓存:磁盘缓存 去获取缓存图片
<--分析9:decodeFromCache() -->
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
result = decodeJob.decodeResultFromCache();
// 获取磁盘缓存时,会先获取 转换事后图片 的缓存
// 即在使用磁盘缓存时设置的模式,若是设置成DiskCacheStrategy.RESULT 或DiskCacheStrategy.ALL就会有该缓存
// 下面来分析decodeResultFromCache() ->>分析10
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
// 若是获取不到 转换事后图片 的缓存,就获取 原始图片 的缓存
// 即在使用磁盘缓存时设置的模式,若是设置成DiskCacheStrategy.SOURCE 或DiskCacheStrategy.ALL就会有该缓存
// 下面来分析decodeSourceFromCache() ->>分析12
}
return result;
}
<--分析10:decodeFromCache() -->
public Resource<Z> decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
Resource<T> transformed = loadFromCache(resultKey);
// 1. 根据完整的缓存Key(由10个参数共同组成,包括width、height等)获取缓存图片
// ->>分析11
Resource<Z> result = transcode(transformed);
return result;
// 2. 直接将获取到的图片 数据解码 并 返回
// 由于图片已经转换过了,因此不须要再做处理
// 回到分析9原处
}
<--分析11:decodeFromCache() -->
private Resource<T> loadFromCache(Key key) throws IOException {
File cacheFile = diskCacheProvider.getDiskCache().get(key);
// 1. 调用getDiskCache()获取Glide本身编写的DiskLruCache工具类实例
// 2. 调用上述实例的get() 并 传入完整的缓存Key,最终获得硬盘缓存的文件
if (cacheFile == null) {
return null;
// 若是文件为空就返回null
}
Resource<T> result = null;
try {
result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
} finally {
if (result == null) {
diskCacheProvider.getDiskCache().delete(key);
}
}
return result;
// 若是文件不为空,则将它解码成Resource对象后返回
// 回到分析10原处
}
<--分析12:decodeFromCache() -->
public Resource<Z> decodeSourceFromCache() throws Exception {
if (!diskCacheStrategy.cacheSource()) {
return null;
}
Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
// 1. 根据缓存Key的OriginalKey来获取缓存图片
// 相比完整的缓存Key,OriginalKey只使用了id和signature两个参数,而忽略了大部分的参数
// 而signature参数大多数状况下用不到,因此基本是由id(也就是图片url)来决定的Original缓存Key
// 关于loadFromCache()同分析11,只是传入的缓存Key不同
return transformEncodeAndTranscode(decoded);
// 2. 先将图片数据 转换 再 解码,最终返回
}
复制代码
Glide
两级缓存机制里都没有该图片缓存时,只能去源头(如网络)去加载图片了
- 此处先忽略该过程 #2. 如有兴趣的同窗请看#该过程在请看文章
Glide
将图片写入 磁盘缓存的时机:获取图片资源后 、图片加载完成前
写入磁盘缓存又分为:将原始图片 写入 或 将转换后的图片写入磁盘缓存
- 从 上篇文章:Android:这是一份全面 & 详细的图片加载库Glide源码分析中,在第3步
into()
中执行图片线程run()
里的decode()
开始(上文的分析13) 此处从新贴出代码
private Resource<?> decode() throws Exception {
// 在执行 加载图片 线程时(即加载图片时),分两种状况:
// 状况1:从磁盘缓存当中读取图片(默认状况下Glide会优先从缓存当中读取,没有才会去网络源读取图片)
// 状况2:不从磁盘缓存中读取图片
// 状况1:从磁盘缓存中读取缓存图片
if (isDecodingFromCache()) {
return decodeFromCache();
// 读取磁盘缓存的入口就是这里,上面已经讲解
} else {
// 状况2:不从磁盘缓存中读取图片
// 即上文讨论的从网络读取图片,不采用缓存
// 写入磁盘缓存就是在 此处 写入的 ->>分析13
return decodeFromSource();
}
}
<--分析13:decodeFromSource() -->
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
// 解析图片
// 写入原始图片 磁盘缓存的入口 ->>分析14
// 从分析16回来看这里
return transformEncodeAndTranscode(decoded);
// 对图片进行转码
// 写入 转换后图片 磁盘缓存的入口 ->>分析17
}
<--分析14:decodeSource() -->
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
final A data = fetcher.loadData(priority);
// 读取图片数据
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
// 对图片进行解码 ->>分析15
} finally {
fetcher.cleanup();
}
return decoded;
}
<--分析15:decodeFromSourceData() -->
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
// 判断是否容许缓存原始图片
// 即在使用 硬盘缓存API时,是否采用DiskCacheStrategy.ALL 或 DiskCacheStrategy.SOURCE
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
// 若容许缓存原始图片,则调用cacheAndDecodeSourceData()进行原始图片的缓存 ->>分析16
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
}
return decoded;
}
<--分析16:cacheAndDecodeSourceData -->
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
...
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
// 1. 调用getDiskCache()获取DiskLruCache实例
// 2. 调用put()写入硬盘缓存
// 注:原始图片的缓存Key是用的getOriginalKey(),即只有id & signature两个参数
// 请回到分析13
}
<--分析17:transformEncodeAndTranscode() -->
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
Resource<T> transformed = transform(decoded);
// 1. 对图片进行转换
writeTransformedToCache(transformed);
// 2. 将 转换事后的图片 写入到硬盘缓存中 -->分析18
Resource<Z> result = transcode(transformed);
return result;
}
<-- 分析18:TransformedToCache() -->
private void writeTransformedToCache(Resource<T> transformed) {
if (transformed == null || !diskCacheStrategy.cacheResult()) {
return;
}
diskCacheProvider.getDiskCache().put(resultKey, writer);
// 1. 调用getDiskCache()获取DiskLruCache实例
// 2. 调用put()写入硬盘缓存
// 注:转换后图片的缓存Key是用的完整的resultKey,即含10多个参数
}
复制代码
Glide
将图片写入 内存缓存的时机:图片加载完成后 、图片显示出来前
写入 内存缓存 的具体地方:上篇文章中当图片加载完成后,会在EngineJob
中经过Handler
发送一条消息将执行逻辑切回到主线程当中,从而执行handleResultOnMainThread()
里
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final EngineResourceFactory engineResourceFactory;
...
private void handleResultOnMainThread() {
...
// 关注1:写入 弱引用缓存
engineResource = engineResourceFactory.build(resource, isCacheable);
listener.onEngineJobComplete(key, engineResource);
// 关注2:写入 LruCache算法的缓存
engineResource.acquire();
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
复制代码
写入 内存缓存分为:写入 弱引用缓存 & LruCache
算法的缓存
- 内存缓存分为:一块使用了
LruCache
算法机制的区域 & 一块使用了 弱引用机制的缓存- 内存缓存只缓存 转换后的图片
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final EngineResourceFactory engineResourceFactory;
...
private void handleResultOnMainThread() {
...
// 写入 弱引用缓存
engineResource = engineResourceFactory.build(resource, isCacheable);
// 建立一个包含图片资源resource的EngineResource对象
listener.onEngineJobComplete(key, engineResource);
// 将上述建立的EngineResource对象传入到Engine.onEngineJobComplete() ->>分析6
// 写入LruCache算法的缓存(先忽略)
engineResource.acquire();
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
<<- 分析6:onEngineJobComplete()() ->>
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
Util.assertMainThread();
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
// 将 传进来的EngineResource对象 添加到activeResources()中
// 即写入了弱引用 内存缓存
}
}
jobs.remove(key);
}
...
}
复制代码
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final EngineResourceFactory engineResourceFactory;
...
private void handleResultOnMainThread() {
...
// 写入 弱引用缓存(忽略)
engineResource = engineResourceFactory.build(resource, isCacheable);
listener.onEngineJobComplete(key, engineResource);
// 写入 LruCache算法的缓存
engineResource.acquire();
// 标记1
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
// 标记2
cb.onResourceReady(engineResource);
}
}
engineResource.release();
// 标记3
}
复制代码
写入 LruCache
算法 内存缓存的原理:包含图片资源resource
的EngineResource
对象的一个引用机制:
acquired
变量 记录图片被引用的次数acquire()
,变量加1上述代码的标记一、标记2 & 下面
acquire()
源码
<-- 分析7:acquire() -->
void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call acquire on the main thread");
}
++acquired;
// 当调用acquire()时,acquired变量 +1
}
复制代码
release()
时,变量减1上述代码的标记3 & 下面
release()
源码
<-- 分析8:release() -->
void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call release on the main thread");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
// 当调用acquire()时,acquired变量 -1
// 若acquired变量 = 0,即说明图片已经再也不被使用
// 调用listener.onResourceReleased()释放资源
// 该listener = Engine对象,Engine.onResourceReleased()->>分析9
}
}
}
<-- 分析9:onResourceReleased() -->
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
...
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
Util.assertMainThread();
activeResources.remove(cacheKey);
// 步骤1:将缓存图片从activeResources弱引用缓存中移除
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
// 步骤2:将该图片缓存放在LruResourceCache缓存中
} else {
resourceRecycler.recycle(resource);
}
}
...
}
复制代码
因此:
acquired
变量 >0 时,说明图片正在使用,即该图片缓存继续存放到activeResources
弱引用缓存中acquired
变量 = 0,即说明图片已经再也不被使用,就将该图片的缓存Key从 activeResources
弱引用缓存中移除,并存放到LruResourceCache
缓存中至此,实现了:
LruCache
算法 的内存缓存至此,Glide
的图片缓存流程解析完毕。
Glide
的图片缓存流程 汇总关于内存缓存 的总结
LruCache
算法机制的内存缓存读取,再从弱引用机制的 内存缓存 读取LruCache
算法机制的内存缓存关于磁盘缓存 的总结
是否读取 取决于
Glide
使用API的设置
是否写入 取决于
Glide
使用API的设置
Glide
实现内存 & 磁盘缓存是根据 图片的缓存Key
进行惟一标识如 七牛云 等等。
Url
地址的基础上再加一个token参数http://url.com/image.jpg?token=a6cvva6b02c670b0a
复制代码
Glide
加载该图片时,会使用加了token
参数的图片Url
地址 做为 id
参数,从而生成 缓存Keytoken
参数可能会发生变化,并非一成不变token
参数变了,则图片Url
跟着变,则生成缓存key的所需id参数发生变化,即 缓存Key也会跟着变化token
参数变化,而致使缓存Key发生变化,从而使得 Glide
的缓存功能失效缓存Key发生变化,即同一个图片的当前缓存key 和 以前写入缓存的key不相同,这意味着 在读取缓存时 没法根据当前缓存key 找到以前的缓存,从而使得失效
具体请看文章:Android 图片加载的那些事:为何你的Glide 缓存没有起做用?
Glide
的图片缓存功能进行流程 & 源码分析Glide
的其余功能进行源码分析 ,有兴趣能够继续关注Carson_Ho的安卓开发笔记