從Glide圖片加載流程深入分析源碼
不知道有沒有小伙伴跟我一樣,使用Glide已經(jīng)有好多年辜限,平時看源碼比較零碎皇拣,對Glide的源碼只能算一知半解,面試遇到分析Glide的源碼薄嫡,無從說起……
那今天就從0開始氧急,對Glide源碼進(jìn)行一個整體的梳理,更進(jìn)一步的了解Glide的原理毫深。
注意:此文章基于Glide 4.11.0版本
眾所周知吩坝,我們使用Gilde加載圖片時,最常用的一行代碼:
Glide.with(context).load(url).into(imageView)
這里就只先分析此行代碼的主線流程哑蔫,省略了其他的配置方法钉寝。
1. Glide.with()
首先從Glide的with
方法開始,它是我們最開始調(diào)用的方法闸迷,有多個重載:
無論是哪個重載方法嵌纲,內(nèi)部都是return了一行 getRetriever().get()
代碼:
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
先調(diào)用 getRetriever
返回了一個 RequestManagerRetriever
對象,然后在調(diào)用 RequestManagerRetriever
的 get
方法返回一個RequestManager
對象
RequestManagerRetriever
的get
方法腥沽,也是有多個重載:
其中最大的區(qū)別就是在于context和非context的參數(shù)的方法逮走,實(shí)現(xiàn)各不相同:
如果是非Context的參數(shù),比如 activity巡球、fragment言沐、view
,其中fragment
和view
都可以找到一個所屬的Activity對象酣栈,那么最后就會調(diào)用到get(Activity activity)
险胰,并內(nèi)部調(diào)用fragmentGet
方法:
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
在fragmentGet
方法中,創(chuàng)建了一個Fragment和RequestManager:
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// ...
}
return requestManager;
}
這里稍微提一下RequestManagerFragment
,getRequestManagerFragment
創(chuàng)建的RequestManagerFragment是Glide用來進(jìn)行圖片加載的生命周期管理的矿筝,最終通過FragmentManager添加到Activity上起便,它本身什么都不展示,只是一個空的Fragment,僅僅是通過Fragment的生命周期來管理圖片加載的請求榆综,避免內(nèi)存泄漏問題妙痹,可以看getRequestManagerFragment
方法的實(shí)現(xiàn):
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
回到主線,我們到這里就從RequestManagerRetriever
中獲得了一個RequestManager
對象鼻疮,從而就可以開始第二步動作:load
2. Glide.with().load()
當(dāng)我們獲得一個RequestManager
后怯伊,就可以接著調(diào)用.load()
的方法,現(xiàn)在的調(diào)用就就到了Glide.with().load()
判沟,可以看到load也是有多個重載方法耿芹,可以支持各種數(shù)據(jù)的load,包括file挪哄、string的url吧秕,uri等等……
load內(nèi)部是通過調(diào)用asDrawable
方法,asDrawable
再調(diào)用了as
方法迹炼,創(chuàng)建了一個RequestBuilder
并返回:
asDrawable()
:
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
as()
:
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
Glide.with().load().into()
這就是我們調(diào)用load方法之后得到RequestBuilder
的由來砸彬,得到RequestBuilder
之后,按照邏輯順序調(diào)用RequestBuilder
的into
方法斯入,也是主線邏輯里面最重要的部分:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// ...
// 構(gòu)建Request請求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// ...
return target;
}
requestManager.clear(target);
target.setRequest(request);
// 執(zhí)行加載請求
requestManager.track(target, request);
return target;
}
這里省略了不相關(guān)的代碼砂碉,into
方法中,主要是構(gòu)建了一個Request
對象咱扣,然后調(diào)用requestManager.track(target, request)
開始加載圖片绽淘,繼續(xù)跟蹤到requestManager.track
方法,位于RequestManager.java
中:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
可以看到request繼續(xù)傳遞到requestTracker.runRequest(request)
中闹伪,可以繼續(xù)跟蹤看看:
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
// ...
}
}
在RequestTracker.java
類里面,runRequest
調(diào)用了request.begain()
方法
由于Request
是一個接口壮池,這里的request
對象在構(gòu)建的時候其實(shí)是構(gòu)建了它的實(shí)現(xiàn)類SingleRequest
偏瓤,位于RequestBuilder.java
:
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
那么在SingleRequest
的begin
方法里面,判斷語句有好幾個椰憋,千萬要迷路厅克,這里的重點(diǎn)是onSizeReady
:
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
// ...
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
// ...
}
}
繼續(xù)跟蹤onSizeReady
,里面使用engine
對象調(diào)用了load
方法:
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
// ...
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
// ...
}
}
繼續(xù)分析Engine的load方法橙依,這里加載請求就揭開了Gilde的緩存機(jī)制:
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 構(gòu)建加載資源的key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
// 從內(nèi)存中加載
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
// 內(nèi)存中沒有就等待或者是開始新的加載
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
首先Glide是 loadFromMemory
從內(nèi)存中加載資源证舟,這里的內(nèi)存細(xì)分一下,分別是 活動資源
和 內(nèi)存緩存
窗骑,Glide是優(yōu)先從活動資源中獲取資源女责,獲取不到再從內(nèi)存緩存中獲取,如果內(nèi)存緩存也獲取不到创译,loadFromMemory
就返回null:
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
// 檢查是否從內(nèi)存緩存中讀取
if (!isMemoryCacheable) {
return null;
}
// 從活動緩存中讀取
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
// 從活動緩存中讀取到了資源
return active;
}
// 從活動緩存中沒有讀取到資源
// 從內(nèi)存緩存中讀取
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
// 從內(nèi)存緩存中讀取讀取到了資源
return cached;
}
return null;
}
這里從內(nèi)存緩存中讀取到了資源之后抵知,還把資源加入到活動緩存,這樣做的目的是為了下一次獲取數(shù)據(jù)更快(先從活動資源獲取數(shù)據(jù)):
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
現(xiàn)在回過頭來看 waitForExistingOrStartNewJob
方法,這個方法是從 loadFromMemory
沒有加載到數(shù)據(jù)時才調(diào)用刷喜,waitForExistingOrStartNewJob
里面構(gòu)建了 engineJob
和 decodeJob
的對象残制,engineJob
對象用來啟動了一個 decodeJob
,engineJob
很簡單掖疮,就是維護(hù)了線程池初茶,進(jìn)行線程的調(diào)度,這里在start
里面執(zhí)行了 decodeJob
:
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
由此看來浊闪,既然是線程調(diào)度恼布,decodeJob
是被執(zhí)行的部分,那DecodeJob
類肯定是實(shí)現(xiàn)了 Runnable 接口规揪,并將主要的邏輯寫到了 run 方法里面桥氏,跟蹤代碼一看,果不其然:
class DecodeJob<R>
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
// ...
}
避免干擾猛铅,我將run
方法里面的注釋和不相關(guān)的邏輯給刪除掉了:
public void run() {
// ...
try {
// ...
// 主要代碼字支,我們繼續(xù)跟蹤這個方法
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
// ...
} finally {
// ...
}
}
繼續(xù)跟蹤runWrapped
方法:
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
// 首次初始化并獲取資源
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
// 從磁盤緩存獲取不到數(shù)據(jù),重新獲取
runGenerators();
break;
case DECODE_DATA:
// 獲取資源成功奸忽,解碼數(shù)據(jù)
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
這里通過我的注釋應(yīng)該很容易理解這個switch語句了堕伪,至于獲取圖片成功后的 decodeFromRetrievedData
,暫時停一下稍候分析栗菜,其他的兩個case語句欠雌,不管是 INITIALIZE
初始化還是 SWITCH_TO_SOURCE_SERVICE
從磁盤緩存獲取不到數(shù)據(jù)進(jìn)行重試,都是要調(diào)用 runGenerators
來獲取數(shù)據(jù)疙筹,我們繼續(xù)跟蹤看看這個方法:
private void runGenerators() {
//...
// 條件語句中的 currentGenerator.startNext() 才是重點(diǎn)
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// ...
}
首先看while的條件語句:
- !isCancelled :如果沒有取消請求
- currentGenerator != null : currentGenerator對象不為空
- !(isStarted = currentGenerator.startNext()) : currentGenerator.startNext()的執(zhí)行結(jié)果會賦值給 isStarted
前兩個不過多解釋富俄,主要是第三個語句:如果isStarted的值為false,表示沒有執(zhí)行成功而咆,就會執(zhí)行while語句體里面的內(nèi)容霍比,while語句體里面就會獲取下一個 Stage
,以及通過Stage
來獲取對應(yīng)的 Generator
:
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
根據(jù)兩個方法聯(lián)合分析暴备,getNextGenerator
執(zhí)行結(jié)果:
- 第一次返回
ResourceCacheGenerator
- 第二次返回
DataCacheGenerator
- 第三次的返回取決于
getNextStage
方法中case DATA_CACHE
返回Stage.FINISHED
或Stage.SOURCE
悠瞬,getNextGenerator
方法就返回SourceGenerator
或者是 null
那其實(shí)這三個Generator:ResourceCacheGenerator
、DataCacheGenerator
涯捻、SourceGenerator
浅妆,都是實(shí)現(xiàn)自DataFetcherGenerator
接口,目的是為了從不同的Generator中加載資源障癌,如果加載成功凌外,就將資源返回,如果加載不成功混弥,就繼續(xù)找下一個Generator加載
不管是從哪個Generator加載數(shù)據(jù)趴乡,都是通過剛才的while語句中的startNext
來執(zhí)行对省,不同的Generator內(nèi)部實(shí)現(xiàn)不一樣,但是都大同小異晾捏,都是加載數(shù)據(jù)蒿涎,加載結(jié)果是通過一個叫做FetcherReadyCallback的接口進(jìn)行回調(diào),這里隨便看一個惦辛,DataCacheGenerator
的startNext
:
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;
// 加載資源重點(diǎn)代碼
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
加載資源的重點(diǎn)代碼是:loadData.fetcher.loadData(helper.getPriority(), this)
loadData.fetcher
獲得的是一個DataFetcher
劳秋,DataFetcher
是獲取資源的接口,所以loadData
具體的工作其實(shí)是交給它的實(shí)現(xiàn)類來完成胖齐,它的實(shí)現(xiàn)類有HttpUrlFetcher
玻淑、LocalUriFetcher
、FileFetcher
呀伙、AssetPathFetcher
等等补履,用于支持各種資源的獲取。
至于加載的細(xì)節(jié)剿另,這里舉兩個例子:
- 從http url加載
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));
}
}
}
- 從文件加載
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
try {
data = opener.open(file);
callback.onDataReady(data);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
callback.onLoadFailed(e);
}
}
加載成功箫锤,通過callback.onDataReady(data)
進(jìn)行回調(diào),加載不成功雨女,則通過callback.onLoadFailed(e)
進(jìn)行回調(diào)谚攒。
以上就是Glide加載圖片的主要流程代碼分析,因?yàn)槭鞘÷粤舜蟛糠值脑创a和注釋氛堕,只保留了主線代碼馏臭,所以還是要自己去跟蹤一遍,才更能理解其中的原理讼稚,謝謝觀看括儒!