【笔记整理】Glide 4.9.0 关于数据加载之后的回调过程

当 Glide 从网络加载原始的数据的时候,会来到 HttpUrlFetcher#loadData() 方法,在 Glide 4.9.0 执行流程源码解析 中说过,当加载完成后,会通过 callback.onDataReady() 方法将结果回传,最终会回溯到 DecodeJob#onDataFetcherReady 这个方法中,下面将会回溯的具体流程进行分析。

// HttpUrlFetcher.java
public void loadData(
    @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
  long startTime = LogTime.getLogTime();
  try {
    // 获取网络图片, 内部使用了 HttpURLConnection 实现, 仅仅做了重定向的处理
    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    // 回调 callback.onDataReady() 方法将结果回传,
    // callback 是在调用方法时传递过来的,这里即 SourceGenerator#startNext() 方法中传递的,即 SourceGenerator 对象自身
    callback.onDataReady(result);
  } catch (IOException e) {
    callback.onLoadFailed(e);
  } finally {
    ...
  }
}

HttpUrlFetcher#loadData() 中得到加载的数据的 InputStream 之后,会将其传入回调方法进行处理。

其中 callback 是在调用方法时传递进来的。而方法是在 SourceGenerator#startNext() 进行调用的。

// SourceGenerator.java
public boolean startNext() {
  ...
  sourceCacheGenerator = null;
  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    // 1. 从 DecodeHelper 的数据加载集合中, 获取一个数据加载器
    loadData = helper.getLoadData().get(loadDataListIndex++);
    if (loadData != null
        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
            || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
      started = true;
      // 2. 使用加载器中 fetcher 执行数据加载
      // 加载网络的 url 资源对应的就是 HttpGlideUrlLoader,
      // 它对应的 ModelLoader.LoadData 中的 fetcher 为 HttpUrlFetcher 类型
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

可以看到看到传递的 callback 即为 SourceGenerator 对象自身(其实现了 DataCallback 接口)。

// SourceGenerator.java
public void onDataReady(Object data) {
  ...
  if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
    // 将数据赋值给 dataToCache
    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.
    // 数据加载回调 onDataReady() 是在子线程中,应该切换回 Glide 的线程。
    
    // 进一步回调,cb 是在 new SourceGenerator 时传递过来的,为 DecodeJob 对象
    cb.reschedule();
  } else {
    ...
  }
}

// 注意 FetcherReadyCallback#reschedule() 的目的就是为了请求在 Glide 所属的线程
// 中再次调用 DataFetcherGenerator#startNext() 方法,
// DataFetcherGenerator 即为 SourceGenerator 的父类。
interface FetcherReadyCallback {
  /**
   * Requests that we call startNext() again on a Glide owned thread.
   */
  void reschedule();
  
  ...
}  

SourceGenerator#cb 是在初始化的时候传递过来的。更具体的,是在 DecodeJob#getNextGenerator() 方法中创建时传递进来的。

// DecodeJob.java
private DataFetcherGenerator getNextGenerator() {
  switch (stage) {
    ...
    case SOURCE:
      // 对应加载的图片的 Generator
      return new SourceGenerator(decodeHelper, this);
    ...
  }
}

public void reschedule() {
  // 将 runReason 状态更新为 RunReason.SWITCH_TO_SOURCE_SERVICE
  runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
  // callback 则是在 `DecodeJob#init()` 时从外部传递进来的对应的 EngineJob 对象
  callback.reschedule(this);
}

可以看到 new SourceGenerator 时第二参数传递的是 DecodeJob 对象自身(其实现了 FetcherReadyCallback 接口)。

因此 SourceGenerator#onDataReady() 中执行 cb.reschedule() 实际上是执行 DecodeJob#reschedule()

然后又会进一步调用 callback.reschedule(this)callback 则是在 DecodeJob#init() 时从外部传递进来的,实际上为对应的 EngineJob 对象。

具体是在 Engine#load() 中,当内存缓存中没有获取到目标资源时,就会进一步从磁盘或者网络获取资源。此时就会构建 EngineJob 与 DecodeJob 对象。

因此在 DecodeJob#reschedule() 又会进一步回调 EngineJob#reschedule()

// EngineJob.java
public void reschedule(DecodeJob<?> job) {
  getActiveSourceExecutor().execute(job);
}

getActiveSourceExecutor() 会获得对应的 GlideExecutor,调用 GlideExecutor#execute() 实际上就是使用 GlideExecutor 内部的线程池来处理 RunnableDecodeJob 实现了 Runnable 接口)。

从而实现了前面(在 SourceGenerator#onDataReady() 方法中)说的从加载资源的子线程切换到 Glide 线程。

因此,此时又会执行 DecodeJob#run() 方法,进而执行 DecodeJob#runWrapped() 方法。

// 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;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}

// 非静态内部类
private enum RunReason {
  /** The first time we've been submitted. */
  INITIALIZE,
  /**
   * We want to switch from the disk cache service to the source executor.
   */
  SWITCH_TO_SOURCE_SERVICE,
  /**
   * We retrieved some data on a thread we don't own and want to switch back to our thread to
   * process the data.
   */
  DECODE_DATA,
}

此时 runReason 已经变为 SWITCH_TO_SOURCE_SERVICE 了(在回调 DecodeJob#reschedule() 被赋值更新的)。

因此此时会在 GlideExecutor 对应的线程池中去执行 runGenerators(),进而调用 currentGenerator.startNext()。从而实现 FetcherReadyCallback#reschedule() 的目的,即切换回 Glide 线程再次调用 startNext()


又回到 SourceGenerator#startNext()

// SourceGenerator.java
public boolean startNext() {
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }
  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
    return true;
  }
  sourceCacheGenerator = null;
  ...
}

// 将数据缓存到磁盘中
private void cacheData(Object dataToCache) {
  long startTime = LogTime.getLogTime();
  try {
    // 这里的 encoder 实际上为 StreamEncoder(在 Glide 的构造方法中注册的,用于处理 InputStream 类型的数据的)
    Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
    DataCacheWriter<Object> writer =
        new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
    originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
    // 缓存到磁盘中
    helper.getDiskCache().put(originalKey, writer);
  } finally {
    loadData.fetcher.cleanup();
  }
  // 为 sourceCacheGenerator 赋值为 DataCacheGenerator 对象
  sourceCacheGenerator =
      new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), 
      		helper, this);
}

在前面的 SourceGenerator#onDataReady() 被回调的时候,会将加载的数据赋值给 dataToCache,即 dataToCache 此时不为空,因此进入到 if 语句中。

cacheData() 方法中,会将加载的数据缓存到磁盘中,且会为 sourceCacheGenerator 赋值新的 DataCacheGenerator 对象。

因此对于后面的 if 语句,sourceCacheGenerator 不会为空。进入到 sourceCacheGenerator.startNext() 中,即 DataCacheGenerator#startNext() 中。

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
	return true;
}
// DataCacheGenerator.java
public boolean startNext() {
  ...
  
  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
    loadData =
        modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
            helper.getOptions());
    if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
      started = true;
      // 这里的 fetcher 为 FileFetcher,调用其 loadData() 的时候又会把自身传递过去
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

注意,在调用 FileFetcher#loadData() 的时候,会把 DataCacheGenerator 对象自身传递过去,因为其实现了 DataFetcher.DataCallback 接口。

// FileFetcher.java
public void loadData(@NonNull Priority priority, 
	@NonNull DataCallback<? super Data> callback) {
  try {
    data = opener.open(file);
  } catch (FileNotFoundException e) {
    callback.onLoadFailed(e);
    return;
  }
  callback.onDataReady(data);
}

根据 File file 得到 data,又会回调 DataCacheGenerator#onDataReady()

// DataCacheGenerator.java
public void onDataReady(Object data) {
  cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}

而成员变量 DataCacheGenerator#cb 则是通过 DataCacheGenerator 的构造方法传递进来的,具体是在 SourceGenerator#cacheData() 中传递的,实际上就是 SourceGenerator 对象自身。

// SourceGenerator.java
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
    DataSource dataSource, Key attemptedKey) {
  // This data fetcher will be loading from a File and provide the wrong data source, so override
  // with the data source of the original fetcher
  cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}

SourceGenerator#cb 在前面也说过,同样是在 new 的时候传递进来的,即 DecodeJob 对象,因此最终会回调搭到 DecodeJob#onDataFetcherReady()

注意,到目前为止,是在前面切换到 GlideExecutor 的线程池的线程中来执行的。

到这里,就是从 HttpUrlFetcher#loadData() 最终回溯到 DecodeJob#onDataFetcherReady() 的具体流程。

你可能感兴趣的:(读书笔记)