前言
閱讀源碼應(yīng)該認準一個功能點(主線),然后去分析這個功能點是如何實現(xiàn)的。千萬不要試圖去搞懂每一行代碼都是什么意思,那樣很容易會陷入到源碼中庄吼,而且越陷越深。
Glide 使用簡明的流式語法API严就,在大部分情況下一行代碼即可搞定需求:
Glide.with(fragment)
.load(url)
.into(imageView);
取消加載同樣很簡單:
Glide.with(fragment).clear(imageView);
盡管及時取消不必要的加載是很好的習(xí)慣总寻,但這并不是必須的操作。實際上梢为,當 Glide.with()
中傳入的 Activity 或 Fragment 實例銷毀時渐行,Glide 會自動取消加載并回收資源。Glide的使用非常簡單铸董,但是簡單的背后祟印,隱藏了復(fù)雜的實現(xiàn),源碼內(nèi)容非常多粟害。
Glide.with
Glide類是單例模式蕴忆,其中重載了多個with
方法,允許我們傳遞上下文(Context)以及能夠獲得上下文的Fragment與View悲幅。
所有的with
方法中的實現(xiàn)均為:
//Glide
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
主要做了兩件事情:
- 通過
get
方法獲得Glide單例對象并獲取其成員屬性:RequestManagerRetriever套鹅; - 通過RequestManagerRetriever的
get
方法獲得RequestManager。
RequestManagerRetriever
RequestManagerRetriever的功能就是獲得RequestManager請求管理者汰具。Glide中所有加載圖片的請求都是由請求管理器進行管理卓鹿。為了進行自動生命周期的管理,請求管理器與Glide.with
方法傳遞的不同參數(shù)相對應(yīng)留荔。
Glide.with
參數(shù):
- 傳遞Activity吟孙,則此次加載圖片會在退出Activity后自動取消;
- 傳遞Fragment存谎,則加載圖片會在Fragment銷毀時取消拔疚;
- 傳遞Application,那么Glide將無法管理圖片請求生命周期既荚。
換句話說稚失,每一個Activity存在一個對應(yīng)的RequestManager,每一個不同的Fragment也有其對應(yīng)的RequestManager恰聘,而整個應(yīng)用運行階段同樣會有一個RequestManager句各。這些不同的RequestManager
通過RequestManagerRetriever.get
獲取。RequestManagerRetriever同樣重載了多個get方法晴叨,以get(FragmentActivity)
為例:
//RequestManagerRetriever
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
- 若非主線程凿宾,則使用Application上下文調(diào)用get重載方法
get(Context)
,直接返回自己的成員屬性RequestManager類型的applicationManager
兼蕊; - 若在主線程初厚,則獲得Activity對應(yīng)的FragmentManager來獲得RequestManager。
對于主線程的情況,Glide為了進行生命周期管理产禾,在Activity#onDestory時取消圖片加載排作。那么如何監(jiān)聽到Activity的onDestory回調(diào)?如果向此Activity中添加一個Fragment亚情,當Activity銷毀時妄痪,Activity中附加的Fragment同樣也會銷毀。事實上實現(xiàn)也確實如此楞件,在獲得了Activity對應(yīng)的FragmentManager之后衫生,supportFragmentGet
的實現(xiàn)為:
//RequestManagerRetriever
private RequestManager supportFragmentGet(@NonNull Context context,@NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
其中getSupportRequestManagerFragment(fm, parentHint, isParentVisible)
就是獲得附加在對應(yīng)Activity的Fragment,而此Fragment中存在成員RequestManager土浸。如果此成員不為null罪针,則直接返回,否則創(chuàng)建并設(shè)置進入Fragment之后再返回栅迄。
小結(jié)
- 構(gòu)建 Glide 實例;
- 獲取 RequestManagerRetriever 對象;
- 構(gòu)建 RequestManager 對象站故。
- 若傳遞Activity/Fragment并為主線程調(diào)用, 則為 Activity/Fragment 添加一個 RequestManagerFragment, 其內(nèi)部含有 ReqeustManager 對象;
- 否則, 獲取一個相當于單例的
applicationManager
專門用于處理這類請求。
RequestManager.load
Glide.with
獲得RequestManager之后毅舆,執(zhí)行l(wèi)oad方法設(shè)置圖片源西篓。圖片源可以是:圖片數(shù)據(jù)字節(jié)數(shù)組、File文件憋活,網(wǎng)絡(luò)圖片地址等岂津。以load(String)
為例:
//RequestManager
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
load方法默認設(shè)置目標資源為 Drawable,獲得一個RequestBuilder悦即。緊接著調(diào)用RequestBuilder.load
方法記錄加載的model(圖片源)吮成。
//RequestBuilder
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
RequestBuilder是請求構(gòu)建者,用戶可以使用它設(shè)置如:單獨的緩存策略辜梳、加載成功前占位圖粱甫、加載失敗后顯示圖片等等加載圖片的各種配置。當RequestBuilder 構(gòu)建完成之后作瞄,接下來就等待執(zhí)行這個請求茶宵。
RequestBuilder.into
使用Glide最簡單的方式加載圖片最后一個階段就是執(zhí)行into方法。從into方法為入口開始執(zhí)行圖片加載宗挥,邏輯也開始復(fù)雜起來乌庶。
//RequestBuilder
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
一般來說,我們在執(zhí)行into時傳入一個ImageView用于顯示契耿。在這個into方法中瞒大,先確定本次加載的BaseRequestOptions,然后執(zhí)行重載的另一個into方法搪桂。其中BaseRequestOptions就是上面我們提到的RequestBuilder可以設(shè)置圖片加載的各種配置透敌,這些配置選項就被封裝在BaseRequestOptions中(RequestBuilder extends BaseRequestOptions)。而重載的into方法實現(xiàn)為:
//RequestBuilder
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
這個into方法中首先調(diào)用 buildRequest 構(gòu)建了一個請求Request,然后把Request交給RequestManager跟蹤(生命周期)并啟動請求酗电。