瞭解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使用對象池創建的過程