基于 Glide 5.x 的源码简要分析(整理版)

7 阅读5分钟

基于 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 会按以下顺序查找内存:

  1. Active Resources (活跃资源) :当前正在使用的图片(引用计数法)。
  2. Memory Cache (LruCache) :最近使用过但已不在屏幕显示的图片。

如果内存中没有,则启动 EngineJobDecodeJob 进行异步加载。

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,在线程池中运行。它使用状态机模式来寻找数据源。

状态流转顺序:

  1. ResourceCacheGenerator: 找磁盘中处理过(裁剪/变换后)的缓存。
  2. DataCacheGenerator: 找磁盘中原始数据的缓存。
  3. SourceGenerator: 从源头(网络/本地文件)获取数据。

Java

// DecodeJob.javaprivate 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.javapublic 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.javavoid 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

  1. 解码 (Decode) : BitmapFactory.decodeStream 将数据流转为 Bitmap。

  2. 变换 (Transform) : 如果配置了 CenterCrop/圆角,在此处对 Bitmap 进行处理。

  3. 转码 (Transcode) : 将 Bitmap 包装为 BitmapDrawableGifDrawable

  4. 回调 (Callback) :

    • EngineJob 收到解码后的资源,通过 Handler 切换回主线程。
    • 调用 SingleRequest.onResourceReady
    • 最终调用 Target.onResourceReady,执行 view.setImageDrawable()

总结 Glide 的高效设计

  1. 生命周期感知:自动暂停/恢复请求,防止内存泄漏。
  2. 三级缓存:内存(活跃+Lru) -> 磁盘(处理图+原图) -> 网络。
  3. EngineJob 分治EngineJob 管理线程和回调,DecodeJob 专注复杂的解码状态流转。
  4. 复用机制:BitmapPool 复用位图内存,减少 GC 抖动。