【筆記整理】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() 的具體流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章