Android-圖片加載框架Glide主線分析

深入主流框架源碼,有利于思維拓展,以及遇到問題能究其根本,所以今天就來扒一扒Glide的基本主線。

Glide框架之所有受歡迎,一是因為它的加載支持生命周期管理,二是支持gif加載,三是占用內(nèi)存小(默認配置圖片編碼格式為rgb565),內(nèi)部使用了多級緩存。但是目前也發(fā)現(xiàn)有缺點,實際應(yīng)用中發(fā)現(xiàn)對于gif的支持不是特別好,有時候幀數(shù)會出一些問題。

慣例,先說依賴配置:implementation'com.github.bumptech.glide:glide:4.7.1'

以下是基本使用代碼,先看看Glide.with()方法都干了些啥

activiy

@NonNull

public static RequestManager with(@NonNull FragmentActivity activity) {

return getRetriever(activity).get(activity);

}

@NonNull

private static RequestManagerRetriever getRetriever(@Nullable Context context) {

Preconditions.checkNotNull(context, "You cannot start a load on a not yet attached View or a Fragment where getActivity() returns null (which usually occurs when getActivity() is called before the Fragment is attached or after the Fragment is destroyed).");

? ? return get(context).getRequestManagerRetriever();

}

@NonNull

public static Glide get(@NonNull Context context) {

if (glide ==null) {

Class var1 = Glide.class;

? ? ? ? synchronized(Glide.class) {

if (glide ==null) {

checkAndInitializeGlide(context);

? ? ? ? ? ? }

}

}

return glide;

}

private static void checkAndInitializeGlide(@NonNull Context context) {

if (isInitializing) {

throw new IllegalStateException("You cannot call Glide.get() in registerComponents(), use the provided Glide instance instead");

? ? }else {

isInitializing =true;

? ? ? ? initializeGlide(context);

? ? ? ? isInitializing =false;

? ? }

}

private static void initializeGlide(@NonNull Context context) {

initializeGlide(context, new GlideBuilder());

}

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {

//以上非關(guān)鍵代碼注釋

Glide glide = builder.build(applicationContext);

? ? //以下非關(guān)鍵代碼注釋

? ? Glide.glide = glide;

}

從以上調(diào)用鏈可以看出,with方法在Glide的initializeGlide中,創(chuàng)建了Glide類中的靜態(tài)單例對象glide,然后with方法里面第一個函數(shù)getRetriever(activity)返回的是一個RequestManagerRetriever,這個是啥,暫時先不管,解析來看調(diào)用的第二個函數(shù),RequestManagerRetriever的get方法。這里會調(diào)用supportFragmentGet方法,

@NonNull

public RequestManager get(@NonNull FragmentActivity activity) {

if (Util.isOnBackgroundThread()) {

return this.get(activity.getApplicationContext());

? ? }else {

assertNotDestroyed(activity);

? ? ? ? androidx.fragment.app.FragmentManager fm = activity.getSupportFragmentManager();

? ? ? ? return this.supportFragmentGet(activity, fm, (Fragment)null, isActivityVisible(activity));

? ? }

}

@NonNull

private RequestManager supportFragmentGet(@NonNull Context context, @NonNull androidx.fragment.app.FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {

SupportRequestManagerFragment current =this.getSupportRequestManagerFragment(fm, parentHint, isParentVisible);

? ? RequestManager requestManager = current.getRequestManager();

? ? if (requestManager ==null) {

Glide glide = Glide.get(context);

? ? ? ? requestManager =this.factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);

? ? ? ? current.setRequestManager(requestManager);

? ? }

return requestManager;

}

從RequestManagerRetriever#supportFragmentGet這個方法以及函數(shù)內(nèi)部調(diào)用的第一個方法getSupportRequestManagerFragment可以看出,這里創(chuàng)建了一個SupportRequestManagerFragment,并且添加到了activity,并且在這里綁定了一個RequestManager,這2個玩意,其實就是Glide能根據(jù)生命周期管理請求的關(guān)鍵東東,后面會說。

到這里先總結(jié)一下,Glide.with做了幾件事情:

1. 實例化了Glide對象

2.創(chuàng)建了一個名為SupportRequestManagerFragment的無UI的Fragment,與我們的activity進行關(guān)聯(lián),并且為SupportRequestManagerFragment設(shè)置了一個名為RequestManager的對象

--------------------------------------------------------------------------------------------

接下來再看load方法,load方法構(gòu)建了一個RequestBuilder,暫時先不看,我們先看下一個方法,也就是into(設(shè)置到某個Imageview)上,看源碼

@NonNull

public ViewTargetinto(@NonNull ImageView view) {

//上面是一些請求配置項,暫不看

return into(

glideContext.buildImageViewTarget(view, transcodeClass),

? ? ? /*targetListener=*/ null,

? ? ? requestOptions);

}

private <Y extends Target<TranscodeType>>Y into(

@NonNull Y target,

? ? @Nullable RequestListener<TranscodeType> targetListener,

? ? @NonNull RequestOptions options) {

//省略。。。

//這里buildRequest構(gòu)建了一個Request對象,深入追蹤會發(fā)現(xiàn),實際創(chuàng)建的是一個叫做SingleRequest的對象,由于代碼調(diào)用比較深,這里就不一一貼出來了

? Request request = buildRequest(target, targetListener, options);

?//省略。。。

//這里是關(guān)鍵方法,這里調(diào)用了RequestManager的track方法,內(nèi)部調(diào)用了RequestTracker的runRequest方法傳進去的就是剛才構(gòu)建的Request(實際是SingleRequest)

??requestManager.track(target, request);

? return target;

}

下面我們看下RequestTracker類的runRequest干了些啥。。。

public void runRequest(@NonNull Request request) {

requests.add(request);

? if (!isPaused) {

request.begin();

? }else {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Paused, delaying request");

? ? }

pendingRequests.add(request);

? }

}


RequestTracker這個類,看到里面管理了2個集合,一個是requests,一個是pendingRequests,有沒有感覺似曾相識的感覺,沒錯,okhttp內(nèi)部也有自己維護的隊列(運行中隊列,待執(zhí)行隊列),不過這里是的維護方式是有區(qū)別的,我們后面說。這里runRequest方法,把singleRequet添加到了requests集合中,判斷如果isPaused,則調(diào)用Request的begin方法,否則添加到pendingRequests中,這里isPaused猜測應(yīng)該是跟頁面生命周期相關(guān)的字段,看下isPause的字段都在哪些地方有賦值,{pauseRequests,pauseAllRequests,resumeRequests},看下這些方法都是在哪些地方有調(diào)用


RequestManager

發(fā)現(xiàn)沒有,又回到了RequestManager的這個類來了,剛才我們說到了,Glide創(chuàng)建的無UI的SupportRequestManagerFragment,里面也塞了一個RequestManger,這里是不是可以認為Glide的生命周期維護是有Activity傳遞給SupportRequestManagerFragment,SupportRequestManagerFragment再傳遞給RequestManager呢?看一下RequestManager的繼承實現(xiàn)關(guān)系,實現(xiàn)了LifecycleListener接口,然后在SupportRequestManagerFragment這個Fragment類中尋找相關(guān)線索,發(fā)現(xiàn)創(chuàng)建的時候有個ActivityFragmentLifecycle這個玩意,這個玩意在SupportRequestManagerFragment的onStart/onStop/onDestory中都有調(diào)用,但是這個東西跟我們的RequestManager有什么關(guān)系呢?我們最終不是調(diào)用的是它嗎,繼續(xù)查看ActivityFragmentLifecycle,發(fā)現(xiàn)它有個addListener方法,看下它的調(diào)用地方,發(fā)現(xiàn)了什么,原來在RequestManager構(gòu)建的時候就把它自己丟給了ActivityFragmentLifecycle,是不是現(xiàn)在一瞬間就清晰了。Activity的生命周期變化傳遞給了SupportRequestManagerFragment,SupportRequestManagerFragment將變化傳遞給了ActivityFragmentLifecycle,ActivityFragmentLifecycle將變化最終給到了RequestManager,然后RequestManager最終調(diào)用到了RequestTracker的resumeRequests/pauseRequests。


ActivityFragmentLifecycle


RequestManager

剛才上面提到過Glide的RequestTracker里面維護的2個集合跟Okhttp中Dispatcher所處理的方式不一樣,Okhttp是任務(wù)執(zhí)行完后會執(zhí)行finish方法,判斷運行中隊列個數(shù)以及待執(zhí)行隊列的情況,進行任務(wù)轉(zhuǎn)移,而Glide是在頁面onStop的時候直接就把所有的請求都給停止了,然后在onStart的時候,把那些沒有完成或者失敗的請求再次開始請求。

上面只提到了Glide的調(diào)用鏈,基本思路,以及生命周期的管理如何實現(xiàn),但是最終的請求,我們還沒有追蹤到,就是SingleRequest的begin方法,這個方法后面的邏輯涉及到后面的多級緩存以及相求相關(guān),這里簡單梳理一下

SignleRequest#begin()->SignleRequest#onSizeReady(overrideWidth, overrideHeight); ? ? -> engine.load(***)這個方法里面涉及到緩存讀取(loadFromActiveResources/loadFromCache),由于時間關(guān)系,凌晨一點多了,后面邏輯有時間再補充,白天上班擼碼,晚上還要回來學習復習鞏固知識,時間有點不夠用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372