Glide 原始碼分析(一)
Glide原始碼分析
寫在前面
在專案開發中一直使用Glide進行圖片的載入顯示,知道如何使用,但不清楚Glide內部是如何實現的。所以一直想看看Glide的原始碼實現是如何做的,終於有時間和機會了,寫下來做個筆記,也給大家學習下。
Glide載入基本步驟
一般而言,我們要載入並顯示一張圖片只需要一行程式碼,如下所示:
Glide.with(Context).load(source).into(target);
很簡單的一行程式碼,但是具體Glide中做了什麼操作,怎麼操作。我們也要進一步瞭解下。
原始碼下載
如果是用新增依賴的方式將Glide引入到專案中的,原始碼就已經下載下來了,在Android Studio中就可以直接進行檢視。 使用新增依賴的方式引入的Glide,只能看原始碼,不能修改原始碼,如果想修改原始碼,可以在GitHub上面將完整原始碼下載下來。
在網上看到大神分析Glide原始碼是3.7.0版本,因為Glide已經更新很多版本了,雖然大部分相同,可是也有細微差異。 所以我這邊分析用的原始碼是4.8.0,需要下載原始碼的點這裡:https://github.com/bumptech/glide/releases/tag/v4.8.0
開始閱讀
按照上面說的Glide的載入步驟,就是三步:先with(),再load(),最後into()。那麼我們開始一步步閱讀這三步走的原始碼,先從with()看起。
public static RequestManager with(@NonNull Context context) { return getRetriever(context).get(context); } public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity); } public static RequestManager with(@NonNull FragmentActivity activity) { return getRetriever(activity).get(activity); } public static RequestManager with(@NonNull Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } public static RequestManager with(@NonNull android.app.Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } public static RequestManager with(@NonNull View view) { return getRetriever(view.getContext()).get(view); }
with() 是一組過載的靜態方法,每個with()方法傳入的引數不同,有fragment、activity、context三種引數。 with() 返回的是RequestManager,所以應該可以猜到,接下來的load()的操作肯定是在RequestManager 中進行。 with() 內部呼叫了getRetriever(),然後又呼叫了get方法。一般,單例模式都是用get方法返回唯一例項的,所以可以想到在get中返回了RequestManager物件 。
getRetriever()中的邏輯是什麼?又做了什麼操作?進來看看。
private static RequestManagerRetriever getRetriever(@Nullable Context context) { ... return Glide.get(context).getRequestManagerRetriever(); }
getRetriever返回型別是RequestManagerRetriever。 在方法內部呼叫Glide的get方法返回單例物件,再呼叫Glide的getRequestManagerRetriever方法,返回RequestManagerRetriever。我們進Glide的get中看具體做了什麼操作。
/**
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
get是靜態方法,返回型別為Glide。 在方法內部採用雙重鎖檢測機制(DCL,單例模式的實現方法之一)檢查並初始化Glide後返回glide唯一例項,glide變數在宣告的時候用volatile關鍵字修飾。再進一步看如何進行檢查和初始化Glide的。
private static void checkAndInitializeGlide(@NonNull Context context) {
// In the thread running initGlide(), one or more classes may call Glide.get(context).
// Without this check, those calls could trigger infinite recursion.
if (isInitializing) {
throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
initializeGlide(context);
isInitializing = false;
}
在方法內呼叫initializeGlide(context),在initializeGlide(context)中又呼叫了initializeGlide(context, new GlideBuilder())。
@SuppressWarnings("deprecation")
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
...
移除相同的GlideModule
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
...
設定Glide Builder
...
Glide glide = builder.build(applicationContext);
...
//註冊元件
...
//註冊記憶體優化的系統回撥
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
到目前為止,glide的初始化已完成,我們拿到了glide的唯一例項。 然後返回到上面的getRequestManagerRetriever,繼續跟蹤它內部是什麼邏輯。
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
拿到Glide的成員變數requestManagerRetriever,繼續返回上一步,跟蹤它的get方法中是什麼邏輯
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
在這裡,我們可以看到with傳進來的引數派上用場了,根據context的型別走不同的分支。 如果context是null,則直接丟擲異常。如果不是在主執行緒或者傳進來的context是Application型別則返回getApplicationManager,Application物件的生命週期就是應用的生命週期,這種情況下它和應用的生命週期是同步的,在應用關閉退出的時候,Glide的載入也會自動終止。如果不是上面的情況則根據context的型別執行對應的執行get()方法。
@NonNull
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));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
可以看到,如果是在後臺執行緒中,則按Application型別處理。不管傳進來是Activity還是fragment,在get方法中最終都呼叫了supportFragmentGet方法,
@NonNull
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;
}
在supportFragmentGet方法中建立了一個沒有介面的fragment,它的生命週期和context保持一致,這樣context在銷燬的時候,fragment可以檢測到這個銷燬操作,那麼Glide的載入在fragment銷燬的時候也會同步停止。最終返回了requestManager例項。至此with的流程走完了,與咱們開頭看到的with方法返回RequestManager型別的結果是一致的。