前言
我覺得使用第三方的庫時,要做到 知其然 知其所以然 欲然革之 棄之自然 好吧不裝x了說人話就是,對一個庫的瞭解程度可以化為為這樣幾個等級:
- 首先,要能夠熟練使用它。
- 然後,理解其中的原理,怎麼實現的,思考為什麼這樣實現
- 再然後,在瞭解原理的基礎上,如果想要實現庫本身不支援的功能或者感覺實現的不好,這樣會情況下能夠改造它。
- 最後,會用,原理懂了,能遊刃有餘的更改,還覺得自己的才華得不到施展,那就可以其之自然,也就是自己造輪子。 接下來,第一個階段就先跳過了,跳級進入第二個階段。拆輪子,探一探Glide這個庫時怎麼實現的。
整體Glide是怎樣架構的,大輪子如何轉動起來,通過這篇文章對Glide的整體有個認識然後逐一突破。 (這圖畫我了倆小時....,手動比心,求贊)
1、關於Glide的建立
Glide的簡單使用是下面這樣的:
Glide.with(context).load("http:xxxx").into(target)
複製程式碼
先看看Glide.with()方法做了什麼。Glide的with(x)方法的引數可以是:Context、Activity、Fragment、View。 無論是哪種引數都會直接或者間接的呼叫下面這個方法:
private static RequestManagerRetriever getRetriever(@Nullable Context context){
Preconditions.checkNotNull(context,"..");
return Glide.get(context).getRequestManagerRetriever();
}
複製程式碼
這個方法最終會返回一個RequestManagerRetriever物件。Glide.get(ctx)返回的是一個Glide物件,內部使用的單例模式,程式內Glide是單例,如下。敲黑板,這不就是傳說中的雙重效驗鎖。
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
複製程式碼
找到了Glide建立的地方,Glide.get(ctx)->checkAndInitializeGlide(ctx)->initializeGlide(ctx,glideBuilder),最終建立glide單例物件的重擔落在了initializeGlide(..)方法上
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
GeneratedAppGlideModule annotationGeneratedModulegetAnnotationGeneratedGlideModules();
....
Glide glide = builder.build(applicationContext);
...
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
複製程式碼
我們原始碼中關於使用APT技術動態載入GeneratedAppGlideModule物件然後配置Glide相關程式碼隱去了,篇幅有限這一部分單獨成篇詳細探究。
先看這個方法主要完成了三件事:
- 使用APT技術動態載入GeneratedAppGlideModule物件然後配置Glide。(以後探其究竟)
- 使用構造者模式GlideBuilder建立了了Glide物件
- applicationContext.registerComponentCallbacks(glide),applicationContext註冊了ComponentCallbacks監聽,這裡傳入的上一步中新鮮建立的glide物件,glide實現了ComponentCallbacks2介面,介面中void onTrimMemory(@TrimMemoryLevel int level);的方法會在作業系統記憶體不足的時候會呼叫,我們能想到這是清理快取的好時機。
建立小結
# GlideBuild
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {...}
if (diskCacheExecutor == null) {...}
if (animationExecutor == null) {...}
...
if (memoryCache == null) {...}
if (diskCacheFactory == null) {...}
if (engine == null) {...}
return new Glide(context, engine,memoryCache,bitmapPool,arrayPool...)
}
複製程式碼
如上圖經過層層最終呼叫initializeGlide(),這個方法中使用GlideBuilder物件夠建出Glide物件,值得關注的是他是一個單例。而且當在GlideBuider沒有設定響應引數的時候會生成預設的引數供GlideBuilder建立出Glide物件。
2、關於請求管理
2.1、RequestManagerRetriever
書接上回,我們已經對Glide這個類的建立有了一個大體的認知。
還是那行程式碼,
RequestManager requestManager = Glide.with(this);
複製程式碼
這行程式碼順序的做了三件事
- 1、通過Glide glide = Glide.get(context)最終建立得到了一個Glide單例物件;
- 2、呼叫glide.getRetriever()得到requestManagerRetriever物件
- 3、RequestManager manager = requestManagerRetriever.getRequestManager(context)得到一個RequestManager的物件。
RequestManagerReriever類的註釋
A collection of static methods for creating new RequestManagers or retrieving existing ones from activities and fragment.
這個類的職責就是建立新的RequestManager或者在activity和fragment中檢索出已經存在的。
建立一個RequestManager的資料中很重要的一個就是LifeCycle
- 有兩個類實現了LifeCycle分別是ActivityFragmentLifecycle和ApplicationLifecycle。主要區別就在於,ActivityFragmentLifecycle有感知Fragment/Activity宣告週期的能力。
- 如果傳入的是Fragment或者Acitivy,requestManagerReriever檢視建立一個RequestManagerFragment物件,與其繫結,這樣就實現了對Activity,或者Fragment宣告週期的感知。
- 如果傳入的是ApplicationContext就會傳入有兩個類實現了LifeCycle分別是ActivityFragmentLifecycle和ApplicationLifecycle。
摘了比較核心的一段程式碼標記,加了註釋。
private RequestManager fragmentGet(@NonNull Context context,FragmentManager fm,Fragment parentHint,boolean isParentVisible) {
//獲取一個RequestManagerFragment
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
//試圖在RequestManagerFragment中獲取requestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//如果為空就建立一個並且存到RequestManagerFragment中
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
複製程式碼
注意的點
- RequestManagerReriever什麼時候建立的呢?在GlideBuilder中build方法中new了一個通過Glide的構造方法傳入的。
- 當RequestManagerReriever.get(view)的時候,會查詢view所在的Activity進而去創建立Requestmanager,如果沒有找到所在Activity就使用ApplicationLifecycle構建Requestmanager。
總結下RequestManagerReriever物件建立RequestManager流程。
2.2 RequestManager、RequestBuilder
RequestManager見名知意,管理圖片請求,啟動、暫停、重新啟動請求,依據所在元件的生命週期週期和網路做出恰當的動作
使用RequestManager物件一系列的load()方法最終能夠得到一個RequestBuilder的物件, RequestBuilder是一個泛型類,載入什麼樣的型別圖片資源決定了他是什麼型別,圖片資源的來源可能是Recource、File、或者網路。
RequestBuilder<T> requestBuidlder = requestManager.load(xxxx);
複製程式碼
得到RequestBuilder之後,就可以對請求的圖片資源做一些期望設定
- 比如你希望他是多大的,指定尺寸:override()。
- 希望它使用什麼樣子的裁剪規則:centerCrop(),centerInside();..
- 期望他的快取的機制是怎樣的:diskCacheStrategy()。
RequestBuildler,都可以通過RequestOptions進行配置。ReuqestBuilder繼承了BaseRequestOptions類,BaseRequestOption這個類是連線RequestOption和RequestBuilder的橋樑,通過這個類的物件對RequestOption進行一系列的設定。
Request有三個實現類,RequestCoordinator,SingleRequest和ErrorRequestCoordinator,RequestCoodinator。
- RequestCoordinator中包含兩個Request,一個是 primary, error。RequestCoordinator使用primary發起請求,如果primary請求失敗了,就呼叫error請求。
- SingleReuqest 實現了Request方法,載入來自不同型別的圖片資源。
- ThumbnailRequestCoordinator 同時載入縮圖和原始圖。
#RequestBuilder 構建ErrorRequestCoordinator物件的方法為例。
private Request buildRequestRecursive(..) {
//構建請求控制
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
//構建的主請求
Request mainRequest =(...);
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
//構建錯誤請求
Request errorRequest =
errorBuilder.buildRequestRecursive(..);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
複製程式碼
整個Request構建管理的構成如下
這只是一個整體的流程,關於更詳細的Request管理,包括如果感知宣告週期的,重複請求如何管理,引數設定的原理等等單獨成篇分析。3、Engine
Request物件的begin()方法根據圖片不同的來源去載入,獲取到期望尺寸之後就會呼叫onSizeReady(),傳入一系列引數資訊。
onSizeReady(){
...
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);
...
}
複製程式碼
engin.load()
public synchronized <R> LoadStatus load(...) {
...
EngineJob<R> engineJob =engineJobFactory.build(...);
DecodeJob<R> decodeJob =decodeJobFactory.build(...);
...
engineJob.addCallback(cb, callbackExecutor);//載入
engineJob.start(decodeJob);//解碼,對二進位制資料轉換成圖片根據配置進行編輯
...
return new LoadStatus(cb, engineJob);
}
複製程式碼
EngineJob.load實際載入圖片資料的執行者,載入完之後交給decodeJob處理。內部具體如何實現的單獨成篇分析,如這部分中執行緒池如何使用,Bitmap的使用技巧等。
結
以上對Glide這個優秀的開源庫有了一個整體的認識,大體分為三個部分
- Glide建立
- Request管理
- 載入解析 我們會單獨成篇嗎,還可能會把快取策略的應用縱向進行分析。最後再把我花了兩個小時的整體架構圖放上