Glide之SourceGenerator數據請求和磁盤緩存

先看一下對SourceGenerator的描述:

Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from original source data using registered {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders} and the model provided for the load. 

Depending on the disk cache strategy, source data may first be written to disk and then loaded from the cache file rather than returned directly.

中文意思就是在加載的時候,使用註冊的ModelLoaders和model類型去生成DataFetcher。

其實生成DataFetcher是調用DecodeHelper.getLoadData來完成的。如果滿足條件執行DataFectcher的loadData去獲取數據。看源碼

  @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        startNextLoad(loadData);
      }
    }
    return started;
  }

  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

看loadData = helper.getLoadData().get(loadDataListIndex++);這句話就是從LoadData list中獲取一個LoadData,然後根據磁盤緩存策略,磁盤緩存策略稍後說,先看主線,採用DataFetcher去請求數據。還是Model以String爲例,具體的ModelLoader分析,請看Glide 之 Registry、ModelLoaderRegistry、 MultiModelLoaderFactory、 ModelLoader 分析

根據文章中的分析,我們知道load.fetcher.loadData最終會調用到HttpUrlFetcher.loadData方法

  @Override
  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));
      }
    }
  }

HttpUrlFetcher中是通過HttpURLConnection去獲取網絡數據的,獲取到的InputStream result通過callback返回給了SourceGenerator中,就是上面拿到的Object data對象

  void onDataReadyInternal(LoadData<?> loadData, Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      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.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
  }

看cb.reschedule(),cb是在DecodeJob中實例化SourceGenerator時候傳入的FetcherReadyCallback回調,DecodeJob實現了FetcherReadyCallback,所以,我們看看DecodeJob中的reschedule方法

  @Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }

這個callback是DecodeJob.Callback,EngineJob實現了這個接口,也就是說callback指的就是EngineJob,我們看看EngineJob的reschedule方法

  @Override
  public void reschedule(DecodeJob<?> job) {
    // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
    // up.
    getActiveSourceExecutor().execute(job);
  }

這裏用到了線程池,看Glide之線程池

這裏就是重新執行一遍DecodeJob,實現了Runnable接口。我們看DecodeJob的run方法

先總結一下:上面的流程就是通過HttpUrlFetcher請求回來數據後,先在SourceGenerator中暫存起來dataToCache,然後通過FetcherReadyCallback(DecodeJob實現)->DecodeJob.Callback(EngineJob實現)回調到EngineJob中,重新執行一遍DecodeJob。

public void run() {
    ...
    runWrapped();
    ...
}

 

  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);
    }
  }

看上面DecodeJob的reschedule代碼,我們知道此時runReason是SWITCH_TO_SOURCE_SERVICE

  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      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.
  }

此時DecodeJob中的currentGenerator是SourceGenerator,看到這裏,又回到了SourceGenerator的startNext方法裏面。

那麼問題來了,爲啥要折騰這一圈,在SourceGenarator直接解決不就完事了嗎?註釋中給了說明

We might be being called back on someone else's thread. Before doing anything, we should reschedule to get back onto Glide's thread.

我們分析SourceGenarator的startNext的方法的時候,只分析了下半部分,現在看看上半部分:

 public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    ...
  }

cacheData這個方法就是去寫到磁盤緩存上,我們詳細看看這個方法

  private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      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);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(
            TAG,
            "Finished encoding source to cache"
                + ", key: "
                + originalKey
                + ", data: "
                + dataToCache
                + ", encoder: "
                + encoder
                + ", duration: "
                + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

helper.getSourceEncoder 的分析請看Glide之Registry、EncoderRegistry、Encoder分析

我們先看看helper.getDiskCache()

  DiskCache getDiskCache() {
    return diskCacheProvider.getDiskCache();
  }

我們先研究一下這個DiskCache,看Glide之DiskCache這篇文章

inputStream寫入了文件裏,然後實例化了一個DataCacheGenerator(這個對象只是SourceGenerator中使用的,和DecodeJob執行過程的的那個沒有關係),執行startNext方法。DataCacheGenerator的startNext方法是從磁盤緩存DATA_CACHE中取出存的數據,然後有傳給SourceGenerator(實現了FetcherReadyCallback接口),然後又傳給了DecodeJob(實現了FetcherReadyCallback接口),在DecodeJob對數據進行decode成要展示的數據,傳給EngineJob,在EngineJob中會切換到主線程展示。這時工作線程source-thread-1會依次執行完DataCacheGenerator.startNext、SourceGenerator.startNext、DecodeJob.run方法,退出線程。

 

Glide源碼分析(五),EngineJob與DecodeJob代碼詳細加載過程

 

 

 

 

 

 

 

 

 

 

 

 

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