Glide 缓存解析

Glide 缓存解析

前言

Glide 是 Android 开发中大名鼎鼎的图片加载框架,它通过三级缓存实现了对图片加载的优化,又通过内部的图片池实现了对移动端内存的优化

这篇文章的目的是深入解析 Glide 加载一张来自网络图片时的流程及各种控制优化,基于 Glide V4 分析

简单使用

Glide 加载图片非常简单,一行代码足矣

1
2
3
Glide.with(fragment)
.load(myUrl)
.into(imageView)

那我们以改代码为例,深入了解其加载流程,以及这期间涉及到的缓存模型

先提一些概念性的东西

  • Model:数据源,可能是 url,资源 id 等等
  • Data:源数据,来自于 model
  • Resource:我们所需资源的包装类,内部有对象池的缓存和重用,所持有的资源是我们真正想要的,比如说 bitmap
  • ModelLoader: 将 Model 转化成 Data,ModelLoader 有两个方法,一个 handles 表示是否可以处理这个类型的 Model,如果可以的话就可以通过 buildLoadData 生成一个LoadData,而 LoadData 包含了要用来做缓存的 key,及用来获取数据的 DataFetcher
  • Decoder: Data 转化成 Resource
  • ResourceTranscoder:对原始的 Resource 处理转化,比如说圆角啊,缩略啊
  • Encoder:持久化数据,将原始的 Resource 持久化缓存到磁盘里,还有一个子类 ResourceEncoder,这个则是用来持久化处理过的 Resource 的

后面提到的工具都会在 Registry 里注册,这里展示一个最简单的数据第一次获取流程
数据处理流程

流程解析

请求构建解析

Glide.with 方法

1
2
3
public static RequestManager with(Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}

返回了一个 RequestManager 对象,以供后续的代码调用,而 RequestManager 经过 getRetriever 方法层层追踪可以得知是由 Glide 单例对象中持有的 RequestManagerRetriever 中 RequestManagerFactory 工厂生成的,至于具体是如何生成的先略去不谈

RequestManager.load 方法

1
2
3
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}

通过传入的 model 构建一个加载 Drawable 的 RequestBuilder,还是处于请求构建阶段,那么最关键的加载过程应该追踪到 RequestBuilder.into 方法中了

RequestBuilder.into 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public Target<TranscodeType> into(ImageView view) {
// 检查是否在 Android 主线程调用,会涉及到 view 操作
Util.assertMainThread();
// View 当然不能为空
Preconditions.checkNotNull(view);

RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 通过克隆处理复用问题
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}

这里做了一些校验判断,以及对 ImageView 的特殊处理(根据 transcodeClass 创建对应的 ImageViewTarget,这里应该是 Drawable.class),最终调用了真正的 into 方法

执行加载流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}

options = options.autoClone();
// 构建 request 对象,里面的构建过程比较复杂,这个 request 对象其实是多个 request 组合而成
Request request = buildRequest(target, targetListener, options);
// request 执行时会通过 tag 的形式绑定在 View 上,所以这里可以找到目标 view 以前可能执行过的图片加载请求
Request previous = target.getRequest();
// 如果绑定的前请求等价于新请求,可以直接使用前请求
if (request.isEquivalentTo(previous)) {
// request 需要回收到对象池中等待复用
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and untracking Targets, and obtaining View dimensions that
// are done in the individual Request.
previous.begin();
}
return target;
}

// 先对 target 做一些必要的清理工作
requestManager.clear(target);
// 将 request 绑定到 target 上
target.setRequest(request);
// 将这两者绑定到管理器上,同时触发请求
requestManager.track(target, request);

return target;
}

追踪到 RequestManager.track 中发现最终会调用 Request.begin 方法,由于我们使用的是最简单的调用方式,所以 buildRequest 只是调用最简单的 SingleRequest.obtain 方法(这里采用了 Message 类似的对象池来防止频繁创建对象,因为在设置了缩略图和错误配置时会递归产生 requet 对象),直接追踪到 SingleRequest.begin 方法

SingleRequest.begin 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}

if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}

// 如果我们在加载完成后重新启动加载流程(通常是在类似于 notifyDataSetChanged 的时候会启用一个完全一样的请求)
// 我们可以使用上一次加载到的资源而不是一个新的请求
// 如果需要加载更改后的配置,那么需要对原来目标先进行清除
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}

// 加载必须发生在目标宽高明确之后
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
// 回调
target.getSize(this);
}

// 后续处理,加载占位图
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}

因为图片的加载是必须在宽高都明确了以后才执行,所以我们追踪到 SingleRequest.onSizeReady 方法

SingleRequest.onSizeReady 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Override
public void onSizeReady(int width, int height) {
// 前置判断,并把状态置成 running
stateVerifier.throwIfRecycled();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;

// 根据缩略值计算宽高
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
// 这里才是图片加载的流程,通过引擎加载,上述流程是对加载参数的处理
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}

图片的加载流程有 engine 负责,返回的 loadStatus 则是对这次引擎加载工作的描述,可以通过这个对象去取消此次加载。由于引擎的加载是基于异步的 io 操作,所以将 this 作为回调接口传入了。由于 Glide 的配置项非常多,所以其请求的构建流程也特别复杂,接下去就是真正的引擎加载流程

引擎加载解析

先来聊聊 engine 的主要构成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引擎任务大小
private static final int JOB_POOL_SIZE = 150;
// 引擎任务池
private final Map<Key, EngineJob<?>> jobs;
// 缓存键构造工厂
private final EngineKeyFactory keyFactory;
// 内存缓存
private final MemoryCache cache;
// 引擎任务工厂
private final EngineJobFactory engineJobFactory;
// 活动资源引用池,为防止内存泄漏通过弱引用持有
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
// 资源回收器
private final ResourceRecycler resourceRecycler;
// 本地缓存生产者
private final LazyDiskCacheProvider diskCacheProvider;
// 解码任务工厂
private final DecodeJobFactory decodeJobFactory;
// 资源引用队列
private ReferenceQueue<EngineResource<?>> resourceReferenceQueue;

具体这些是如何工作的,那就要结合加载过程来讲了

Engine.load 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
// 通过 EngineKeyFactory 生成缓存键
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// 从内存中获取缓存资源
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 从活动资源中获取缓存资源
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}

// 从任务池中获取任务
EngineJob<?> current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}

// 新建任务
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
useUnlimitedSourceExecutorPool, useAnimationPool);
DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
// 将新建的任务缓存起来
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);

if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}

从这个流程中我们来详细解析一下 Glide 的缓存策略

缓存键 Cache Keys

1
2
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);

缓存键通过该方法生成,包含至少两个元素

  1. 请求加载的 model(File, Url, Uri)
  2. 一个可选的签名(Signature)

另外(活动资源,内存缓存,资源磁盘缓存)的缓存键还包含一些其他数据,包括:

  1. 宽度和高度
  2. 可选的变换(Transformation)
  3. 额外添加的任何选项(Options)
  4. 请求的数据类型(Bitmap, GIF, 或其他)

活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,内存缓存通常有适应内存选项(Options),比如影响 Bitmap 配置的选项或其他解码时才会用到的参数

为了生成磁盘缓存上的缓存键名称,以上的每个元素会被哈希化以创建一个单独的字符串键名,并在随后作为磁盘缓存上的文件名使用。后面会根据具体的使用场景说明

内存缓存

Engine.loadFromCache 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
// 如果并没有设置内存缓存,则直接返回 null
if (!isMemoryCacheable) {
return null;
}
// 通过 key 获取缓存
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
// 标记被使用次数,并加入到活动资源引用中
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}

Engine.getEngineResourceFromCache 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 从内存缓存中取,取出后移除,因为要添加到活动资源中去
Resource<?> cached = cache.remove(key);

final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /*isMemoryCacheable*/);
}
return result;
}

cache 对象就是 MemoryCache 内存缓存实例,默认的实现是一个 LruCache – 最近最少使用算法,当缓存空间满了的时候,将最近最少使用的数据从缓存空间中删除以增加可用的缓存空间来缓存新内容。

MemoryCache 中有关于资源移除的监听回调,有 engine 实现相关接口,最终回调的是 ResourceRecycler.recycle(resource),最终调用的都是 Resource 自己实现的 recycle 方法释放对象已方便重用,拿最常用的 BitmapResource 来说吧,他就有一个 bitmapPool 用来存放已经释放的 bitmap 资源,新建的时候可以尝试着去 bitmapPool 去取(这里涉及到 inBitmap 相关知识,自行查阅),可以大大缓解内存抖动

活动资源缓存

Engine.loadFromActiveResources 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

活动资源的引用比较简单,是一个参数为弱引用的池子,不用像内存缓存一样考虑缓存大小

磁盘缓存

根据 key 获取加载任务,如果已存在只需要把加载回调加入其中即可,因为相同的 key 代表着相同尅性的加载。如果不存在,再通过工厂新生成一个 EngineJob 和 DecodeJob,将 EngineJob 存入引用池后开始任务

这里要注意的是 glide 会对处理过的图片也进行缓存,默认状态是会先读取处理过的缓存数据,再读取原始缓存数据,最后才会尝试在数据源加载

EngineJob.start(DecodeJob) 方法

1
2
3
4
5
6
7
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}

这里会根据 DecodeJob 中持有的 DiskCacheStrategy 硬盘缓存策略来决定是否从硬盘缓存中读取还是从数据源读取(选取执行的线程池),无论选取哪个线程池,最终执行的都是 DecodeJob 的 run 方法

DecodeJob.run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public void run() {
// 提供一些信息,防止线程池执行异常不报错
TraceCompat.beginSection("DecodeJob#run");
// try catch 保证出错是 localFetcher 会被清理
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
// 执行获取数据的操作
runWrapped();
} catch (Throwable t) {
// 异常捕获,资源释放
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, t);
}
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
} finally {
// 清理 localFetcher
if (localFetcher != null) {
localFetcher.cleanup();
}
TraceCompat.endSection();
}
}

run 方法交由 runWrapped 执行

DecodeJob.runWrapped 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private void runWrapped() {
switch (runReason) {
// 这是初始化状态
case INITIALIZE:
// 根据硬盘缓存策略选择缓存模式
stage = getNextStage(Stage.INITIALIZE);
// 根据缓存模式选择 DataFetcher 生成器
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}

/**
* 根据当前状态和磁盘缓存策略获取下一个状态,那就能实现如果当前的 DataFetcher 抓取不到数据,
* 就可以从下一级抓取数据,默认顺序是处理过的缓存数据->原始缓存数据->数据源数据
*/
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}

/**
* 根据状态寻找 DataFetcherGenerator,用于数据的抓取
*/
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}

默认的模式是从 INITIALIZE 状态开始的,那么就会
DecodeJob.runGenerators 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
// 查找合适的数据来源
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();

// 如果数据来源是 SOURCE 的话,重新执行这个任务,为的是切换执行的线程池
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// 如果达到完成阶段了还没找到对应来源,就是加载失败了
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// 否则的话是会在生成器中启动一个相应的load,并在回调 onDataFetcherReady 中接收到我们想要的数据
}

其实这个过程可以概括为

  1. 取当前状态 stage
  2. 获取当前 stage 下的 DataFetcherGenerator
  3. 执行 DataFetcherGenerator.startNext,如果返回 true 即找到了缓存数据,结束循环,等待 onDataFetcherReady 回调,否则取下一个状态 stage 循环上述过程
  4. Stage.FINISHED 状态且未找到缓存,加载失败

stage 主要包括处理过的缓存,原始数据缓存以及数据源

DataFetcherGenerator.startNext 内的主要逻辑就是生成一个缓存键,通过这个键去查找磁盘中可以缓存的 File,如果存在则再去查找对应的 ModelLoader,最后通过 ModelLoader 中持有的 DataFetcher 去加载数据,以 SourceGenerator 为例,因为它里面还包括了缓存的相关流程

SourceGenerator.startNext 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
public boolean startNext() {
// 这个方法会多次被调用
// 缓存数据相关操作,第一次调用为 null,所以不会执行,先放一边
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
// 第一次调用 null,也不会执行
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;

loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
// 这里是遍历所有注册的 ModelLoader,将创建的 LoadData 放到集合里,找到符合的 LoadData,通过 fetcher 加载数据
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}

fetcher 加载完成会回调通知 onDataReady 方法

SourceGenerator.onDataReady 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
// 如果开启缓存,则会执行 DecodeJob.reschedule
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}

DecodeJob.reschedule 方法

1
2
3
4
5
6
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
// 这边触发 callback.reschedule 会使 DecodeJob 在 ActiveSourceExecutor 上再重新跑一遍
callback.reschedule(this);
}

回到 DecodeJob.runWrapper 方法,由于 runReason = RunReason.SWITCH_TO_SOURCE_SERVICE 所以直接执行了 runGenerators 方法,其实第一次执行 SourceGenerator.startNext 时就已经先执行了这个流程,作为数据源请求,需要在别的线程池上跑

1
2
3
4
5
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}

首先这个过程会被执行

SourceGenerator.cacheData 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
// 通过 Encoder 写入缓存到磁盘
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
// 存入到磁盘缓存中去,默认是 DiskLruCache 实现
helper.getDiskCache().put(originalKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished encoding source to cache"
+ ", key: " + originalKey
+ ", data: " + dataToCache
+ ", encoder: " + encoder
+ ", duration: " + LogTime.getElapsedMillis(startTime));
}
} finally {
loadData.fetcher.cleanup();
}

// 为 sourceCacheGenerator 赋值,可以在 startNext 中继续执行
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}

在 startNext 中,继续执行

1
2
3
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}

待到 sourceCacheGenerator 中对应的 fetcher 执行完毕就会回调 SourceCacheGenerator.onDataReady 方法,再回调 SourceGenerator.onDataFetcherReady 方法,最终回调
DecodeJob.onDataFetcherReady 方法

DecodeJob.onDataFetcherReady 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
// 切换到目标线程执行
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
TraceCompat.beginSection("DecodeJob.decodeFromRet rievedData");
try {
decodeFromRetrievedData();
} finally {
TraceCompat.endSection();
}
}
}

DecodeJob.decodeFromRetrievedData 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> resource = null;
try {
// 这里完成了对图片的处理
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
// 缓存处理后的资源
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}

DecodeJob.notifyEncodeAndRelease 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}

Resource<R> result = resource;
LockedResource<R> lockedResource = null;
// 从对象池里去对象赋值
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
// 资源加载完成回调
notifyComplete(result, dataSource);

stage = Stage.ENCODE;
try {
// 做处理后的资源缓存
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
// 加载过程完成,释放 DecodeJob 对象,加入到复用池中
onEncodeComplete();
}
}

至此,最简单的一个加载流程已经完成,可以用一张图来表示
加载来源获取流程图

结语

本文从一个最简单的图片加载操作完整追踪整个缓存路径,虽然还有许多细节没有涉及到,但确实也是因为 glide 是个非常庞大的图片加载库,为了内存优化、性能改善做了非常多的努力,比如说对列表图片加载的优化等等,希望可以开个好头,激起你探索的兴趣吧