基于 Glide 5.x 的源码简要分析
本文主要梳理 Glide 图片加载的核心流程:从 API 调用到生命周期绑定,再到 Engine 的资源获取与解码,最后展示到 View 上。
第一步:生命周期绑定 (Glide.with)
Kotlin
Glide.with(this).load("url").into(binding.testIv)
核心逻辑:
Glide.with(this) 内部通过 RequestManagerRetriever 获取一个与 this(Activity/Fragment/Context)生命周期绑定的 RequestManager。
- 传入 Activity/Fragment:创建一个无 UI 的 Fragment(SupportRequestManagerFragment),利用该 Fragment 的生命周期回调(onStart/onStop/onDestroy)来管理图片加载请求。
- 传入 Application/非主线程:无法感知页面生命周期,因此绑定 Application 的生命周期(即与 App 共存亡)。
- 传入 View:会通过递归查找该 View 所属的 Activity 或 Fragment 上下文。
第二步:构建请求与参数配置 (into)
RequestBuilder 负责构建请求参数(如占位图、变换等),并根据 ImageView 的 ScaleType 自动应用变换。
Java
// RequestBuilder.java
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
// 根据 ImageView 的 ScaleType 自动设置变换逻辑
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
// ... 其他类型处理
}
}
// 5.x 优化:直接将回调放入 UI 线程执行器中,作为 Request 的参数
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/* targetListener= */ null,
requestOptions,
Executors.mainThreadExecutor());
}
// 构建 Request 并由 RequestManager 追踪
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// 1. 构建 Request 对象(通常是 SingleRequest)
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
// 2. 检查是否与之前的请求相同,如果相同且未完成,可能复用
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
// 3. 清除旧请求,绑定新请求
requestManager.clear(target);
target.setRequest(request);
// 4. 开始追踪请求
requestManager.track(target, request);
return target;
}
第三步:请求启动与状态管理 (RequestManager & Tracker)
RequestManager 将请求委托给 RequestTracker 进行管理,根据当前生命周期状态决定是直接运行还是挂起。
Java
// RequestManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target); // 追踪 Target 生命周期
requestTracker.runRequest(request); // 运行请求
}
// RequestTracker.runRequest 会判断当前状态,若没暂停则调用 request.begin()
// SingleRequest.java (请求的核心实现)
@Override
public void begin() {
synchronized (requestLock) {
startTime = LogTime.getLogTime();
// 1. Model 为空的处理
if (model == null) {
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
// 2. 状态防抖
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// 3. 如果资源已就绪(内存中有),直接回调
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE, false);
return;
}
// 4. 状态流转:WAITING_FOR_SIZE
status = Status.WAITING_FOR_SIZE;
// 5. 获取 View 尺寸
// 只有拿到宽和高,才能去计算采样率和缓存 Key
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
// 通过 ViewTreeObserver 监听 layout 布局完成后回调 onSizeReady
target.getSize(this);
}
// 6. 显示占位图 (Placeholder)
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
第四步:Engine 调度与缓存策略
当 onSizeReady 确定了图片尺寸后,正式进入 Engine(引擎)进行资源加载。
1. 生成缓存 Key
Java
// SingleRequest.onSizeReady -> engine.load()
EngineKey key = keyFactory.buildKey(
model, signature, width, height, transformations, resourceClass, transcodeClass, options);
2. 内存缓存查找
Engine.load 会按以下顺序查找内存:
- Active Resources (活跃资源) :当前正在使用的图片(引用计数法)。
- Memory Cache (LruCache) :最近使用过但已不在屏幕显示的图片。
如果内存中没有,则启动 EngineJob 和 DecodeJob 进行异步加载。
Java
// EngineJob.java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
// 判断是否有磁盘缓存策略,决定使用 diskCacheExecutor 还是 activeSourceExecutor (网络/原始数据)
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
第五步:DecodeJob 状态机 (核心解码流程)
DecodeJob 实现了 Runnable,在线程池中运行。它使用状态机模式来寻找数据源。
状态流转顺序:
- ResourceCacheGenerator: 找磁盘中处理过(裁剪/变换后)的缓存。
- DataCacheGenerator: 找磁盘中原始数据的缓存。
- SourceGenerator: 从源头(网络/本地文件)获取数据。
Java
// DecodeJob.java
private void runWrapped() {
switch (runReason) {
case INITIALIZE: // 初始化状态,寻找下一阶段
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE: // 切换到源码服务(通常指网络下载)
runGenerators();
break;
case DECODE_DATA: // 数据获取成功,开始解码
decodeFromRetrievedData();
break;
// ...
}
}
private void runGenerators() {
boolean isStarted = false;
// 循环尝试不同的 Generator,直到有一个成功启动加载 (return true)
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage); // 状态流转:Resource -> Data -> Source
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
// 如果流转到了 Source 阶段,需要切换线程池(从磁盘线程池 -> 网络线程池)
reschedule(RunReason.SWITCH_TO_SOURCE_SERVICE);
return;
}
}
// ... 失败处理
}
第六步:数据获取与回写 (SourceGenerator)
当磁盘缓存失效,状态机走到 SourceGenerator,开始网络请求。
Java
// SourceGenerator.java
public boolean startNext() {
// 1. 如果有数据需要写入磁盘缓存(来自上一次网络请求),在此处进行
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data); // 写入磁盘缓存 (Source Cache)
// 这里的逻辑是:先下载 -> 放入内存 -> 写入磁盘 -> 重新走 DataCacheGenerator 读取磁盘
}
// 2. 如果刚才写入了缓存,现在通过 sourceCacheGenerator 去读取它
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
// 3. 如果没有缓存任务,则寻找 ModelLoader (如 HttpGlideUrlLoader)
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
// 启动加载:使用 OkHttp/HttpUrlConnection
startNextLoad(loadData);
}
return started;
}
数据回调与重调度
当网络请求成功:
Java
// SourceGenerator.java
void onDataReadyInternal(LoadData<?> loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
// 如果策略允许缓存原始数据 (DATA)
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data; // 暂存数据
cb.reschedule(); // 核心:重新调度 DecodeJob
// DecodeJob 会再次运行 runWrapped() -> runGenerators()
// 这次进入 SourceGenerator.startNext() 时 dataToCache 不为空 -> 写入磁盘 ->
// 这里的妙处在于:Glide 优先解码磁盘中的数据,而不是直接解码网络流,确保一致性。
} else {
// 不缓存,直接解码
cb.onDataFetcherReady(..., data, ...);
}
}
第七步:解码与显示 (补充完整流程)
一旦 DataFetcher 拿到数据(无论是从缓存还是网络),DecodeJob 会调用 decodeFromRetrievedData。
-
解码 (Decode) :
BitmapFactory.decodeStream将数据流转为 Bitmap。 -
变换 (Transform) : 如果配置了 CenterCrop/圆角,在此处对 Bitmap 进行处理。
-
转码 (Transcode) : 将 Bitmap 包装为
BitmapDrawable或GifDrawable。 -
回调 (Callback) :
EngineJob收到解码后的资源,通过Handler切换回主线程。- 调用
SingleRequest.onResourceReady。 - 最终调用
Target.onResourceReady,执行view.setImageDrawable()。
总结 Glide 的高效设计
- 生命周期感知:自动暂停/恢复请求,防止内存泄漏。
- 三级缓存:内存(活跃+Lru) -> 磁盘(处理图+原图) -> 网络。
- EngineJob 分治:
EngineJob管理线程和回调,DecodeJob专注复杂的解码状态流转。 - 复用机制:BitmapPool 复用位图内存,减少 GC 抖动。