本文基于Android 【手撕Glide】–Glide缓存机制,进一步分析 DecodeJob#runWrapped 从磁盘中加载图片的过程。本文涉及到如何从网络加载图片,和写入 DATA 磁盘缓存、RESOURCE 磁盘缓存、ActiveResources 缓存的时机,以及如何从 ModelLoaderRegistry 中获取 ModelLoader。
前情提要: 在内存缓存没有命中🎯的时候,Engine 会创建 EngineJob 和 DecodeJob,在 EngineJob 的线程池中执行 DecodeJob,执行到 DecodeJob#runWrapped 来加载图片 (Engine#load -> Engine#waitForExistingOrStartNewJob -> EngineJob#start -> DecodeJob#runWrapped)。
DecodeJob 主要过程
private void runWrapped() {
switch (runReason) {
case INITIALIZE: // 初始状态,依次访问磁盘缓存 RESOURCE 和 DATA
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE: // 网络下载图片成功时,会走到这里
runGenerators();
break;
case DECODE_DATA: // 成功加载数据源后,解码到 Resource
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
// startNext 返回 false 就继续到下一个 State 和生成器❤️
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule(); // 从网络中加载前,这里要切换线程池啦❤️
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
- 初始 Stage 为 INITIALIZE,那么下一个 Stage 为 RESOURCE_CACHE,下一个生成器为 ResourceCacheGenerator,主要在其 #startNext 方法,创建 ResourceCacheKey,从磁盘中读取缓存,一开始当然没有缓存,那么 modelLoaders 为 null,导致马上返回 false;
- 下一个 Stage 为 DATA_CACHE,下一个生成器为 DataCacheGenerator,在其 #startNext 方法,创建 DataCacheKey,从磁盘中读取缓存,一开始也没有缓存,那么 modelLoaders 为 null,导致马上返回 false;
- 下一个 Stage 为 SOURCE,下一个生成器为 SourceGenerator,在其 #startNext 方法,一开始 dataToCache 为 null,sourceCacheGenerator 为 null,在这里终于去获取 ModelLoader,假设我们是设置 “https://picture.jpg" 这样的图片网络地址,那么最后从 Glide 配置的 ModelLoaderRegistry 中解析出来的就是 HttpGlideUrlLoader,并根据我们的网络地址字符串创建了 GlideUrl 对象作为 sourceKey,HttpGlideUrlLoader 会通过 HttpUrlFetcher 从网络加载图片
// HttpUrlFetcher
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
- 加载成功,会从网络连接中得到 InputStream 回调到 SourceGenerator#onDataReadyInternal,将 InputStream 保存到 dataToCache ,最后调用 DecodeJob#reschedule
public void reschedule() {
runReason