Glide之線程池

Glide裏面的幾個線程池

Glide 源碼解析 之 線程池

瞭解Glide的線程池,先看上面兩篇文章,之後我們說一下,在實際的一次請求過程中,線程池是怎麼用的?只看最一般默認的流程:

一次網絡請求,從Engine的load方法開始,其中會獲取到EngineJob和DecodeJob實例,engineJob.start(decodeJob)就開始了流程

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

看一個decodeJob的willDecodeFromCache方法:

  boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
  }

這塊就不展開了,默認是返回true的,返回到EngineJob的start方法,executor是diskCacheExecutor,diskCacheExecutor是一個單線程的線程池,decodeJob實現了Runnable接口,executor.execute(decodeJob)就是在diskCacheExecutor的線程中執行decodeJob。

這裏不具體展開DecodeJob的執行過程,大概了執行ResourceCacheGenerator的startNext方法從RESOURCE_CACHE(在磁盤中)中取數據,沒有取到執行DataCacheGenerator的startNext方法從DATA_CACHE(在磁盤中,存的原始數據)中取數據,這些操作都是在diskCacheExecutor的線程池中進行的,如果在DATA_CACHE中也沒取到,就要執行SourceGenerator的startNext方法從網絡(具體是由ModelLoader來決定的)去加載數據,注意重點來了,執行SourceGenerator的startNext方法,需要切換線程,看下面代碼:

  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.
  }

我們看stage等於Stage.SOURCE時,說明currentGenerator就是SourceGenerator了,看reschedule

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

runReason變成了SWICH_TO_SOURCE_SERVICE,callback是EngineJob(實現了DecodeJob.Callback接口),我們看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);
  }

  private GlideExecutor getActiveSourceExecutor() {
    return useUnlimitedSourceGeneratorPool
        ? sourceUnlimitedExecutor : (useAnimationPool ? animationExecutor : sourceExecutor);
  }

默認是sourceExecutor,sourceExecutor最多有4個線程,根據手機的CPU個數來決定。看到沒?DecodeJob這個Runnable又“跑到”sourceExecutor線程池裏執行了,需要說明一下上面說的DecodeJob的run方法會在diskCacheExecutor線程中執行完的,不影響。大家想想是爲什麼?這是應該不同線程都有自己的執行棧,每個方法的調用狀態都在對應線程的棧裏面。

在SourceExecutor線程池裏執行SourceGenerator的startNext方法,看這篇文章Glide之SourceGenerator請求數據的過程和緩存到磁盤的過程

請求網絡數據是在SourceExecutor的線程0裏面,數據請求回來:

  @Override
  public void onDataReady(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);
    }
  }

根據DiskCacheStrategy,默認是AUTOMATIC,網絡請求數據isDataCacheable是true

    public boolean isDataCacheable(DataSource dataSource) {
      return dataSource == DataSource.REMOTE;
    }

看cb.reschedule,這個cb是DecodeJob(實現了DataFetcherGenerator.FetcherReadyCallback接口),看看DecodeJob的reschedule方法:

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

這個方法我們分析過,就是回到EngineJob中,用SourceExecutor線程池第二次執行DecodeJob(這是第三次執行DecodeJob),根據上面的分析,我們知道還會執行到SourceGenerator的startNext方法,這次只是走了保存到本地緩存的邏輯。

總結一下:

第一次執行DecodeJob,是嘗試從磁盤緩存RESOURCE_CACHE或者DATA_CACHE中獲取數據,這些工作是在DiskCacheExecutor的線程池中做的,這是一個單線程池,如果沒有獲得數據。reschedule,通過到EngineJob中切換到SourceExecutor線程池

第二次執行DecodeJob,在SourceExecutor線程池中,執行SourceGenerator的startNext去網絡獲取數據。

第三次執行DecodeJob,獲取到網絡數據後,需要再reschedule一遍,讓SourceExecutor線程池去保存數據到磁盤,可以不是同一個線程,具體看SourceExecutor的實現,我這個手機SourceExecutor中有4個線程,第二次和第三次是在同一個線程池中的不同線程中。

這三次執行,執行的是同一個DecodeJob對象。

 

 

 

 

 

進一步學習內容

(1)ThreadPoolExecutor內部實現、ExecutorService的具體用處

(2)EngineJob和DecodeJob使用對象池創建的過程

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