深入Glide 1--Glide框架結構與生命周期管理

上兩篇文章主要介紹Glide的基礎,緩存以及圖片的加載過程舌涨,接下來我們開始正式進入Glide的講解糯耍,通過下面的學習,我們主要會了解到

  • Glide如何綁定Activity囊嘉、Fragment生命周期變化
  • Glide如何監(jiān)聽內存變化温技、網(wǎng)絡變化
  • Glide如何處理請求的生命周期

生命周期相關UML類圖

Glide流程.png

生命周期綁定

Glide生命周期綁定是從入口單例類Glide開始的,通過with()多個重載方法來實現(xiàn)對生命周期的綁定工作扭粱。

public static RequestManager with(Fragment fragment)  
public static RequestManager with(FragmentActivity activity)  
public static RequestManager with(Activity activity)  
public static RequestManager with(Context context)

以Activity的參數(shù)為例:

public static RequestManager with(Activity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
}

RequestManagerRetriever是一個單例類舵鳞,可以理解為一個工廠類,通過get方法接收不同的參數(shù)琢蛤,來創(chuàng)建RequestManager蜓堕。

public RequestManager get(Activity activity) {
    if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        android.app.FragmentManager fm = activity.getFragmentManager();
        return fragmentGet(activity, fm);
    }
}
public RequestManager get(android.app.Fragment fragment) {
    if (fragment.getActivity() == null) {
        throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
    }
    if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return get(fragment.getActivity().getApplicationContext());
    } else {
        android.app.FragmentManager fm = fragment.getChildFragmentManager();
        return fragmentGet(fragment.getActivity(), fm);
    }
}

如果是在子線程進行的with操作,那么Glide將默認使用ApplicationContext博其,可以理解為不對請求的生命周期進行管理套才,通過Activity拿到FragmentManager,并將創(chuàng)建RequestManager的任務傳遞下去贺奠。最終都走到了fragmentGet方法霜旧,注意細微區(qū)別是Activity傳的參數(shù)的是Activity的FragmentManager,F(xiàn)ragment傳的參數(shù)的是ChildFragmentManager儡率,這兩者不是一個東西挂据。

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
    //獲取RequestManagerFragment,并獲取綁定到這個fragment的RequestManager
    RequestManagerFragment current = getRequestManagerFragment(fm);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
    //如果獲取RequestManagerFragment還沒有綁定過RequestManager儿普,那么就創(chuàng)建RequestManager并綁定到RequestManagerFragment
        requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
        current.setRequestManager(requestManager);
    }
    return requestManager;
}

創(chuàng)建RequestManagerFragment

這個方法創(chuàng)建了一個fragment崎逃,并且創(chuàng)建并綁定了一個RequestManager,看看getRequestManagerFragment如何獲取的RequestManagerFragment眉孩。

RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
    //嘗試根據(jù)id去找到此前創(chuàng)建的RequestManagerFragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        //如果沒有找到个绍,那么從臨時存儲中尋找
        current = pendingRequestManagerFragments.get(fm);
        if (current == null) {
            //如果仍然沒有找到,那么新建一個RequestManagerFragment浪汪,并添加到臨時存儲中巴柿。
            //然后開啟事務綁定fragment并使用handler發(fā)送消息來將臨時存儲的fragment移除。
            current = new RequestManagerFragment();
            pendingRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}

這里有個問題死遭,為什么需要使用pendingRequestManagerFragments這樣一個集合來臨時存儲一下fragment广恢,然后又馬上通過handler發(fā)送消息移除?這其實是跟主線程的Looper機制和Fragment的事務機制有關的呀潭。我們知道钉迷,android中的主線程是一個閉環(huán)至非,通過Handler發(fā)送消息到MessageQueue,然后通過Looper輪詢獲取消息并交給Handler處理糠聪。如下面一個常見場景:

Glide.with(this).load(url_1).into(mImageView_1);
Glide.with(this).load(url_2).into(mImageView_2);

這段代碼通過Glide加載了兩張圖片并設置到了兩個ImageView上荒椭,當以上代碼塊執(zhí)行時,其所屬的代碼群的Message剛剛從MessageQueue中取出正在被處理舰蟆,我們假設這個Message為m1趣惠,并且這個MessageQueue中沒有其他消息。此時情形是這樣的:

但是m2這個消息并不會馬上被處理夭苗,這是因為m1還有代碼還沒有執(zhí)行完畢信卡,也就是說這個fragment并不會馬上被綁定,此時m1繼續(xù)向下執(zhí)行到第二句代碼

Glide.with(this).load(url_2).into(mImageView_2);

當這句代碼走到getRequestManagerFragment時题造,如果在m1時傍菇,我們不將fragment臨時存儲在pendingRequestManagerFragments中,由于m2還沒有被處理界赔,那么

RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

必然是找不到這個fragment的丢习,那么就會導致重新創(chuàng)建一個新的重復的fragment,并開啟事務綁定淮悼,這顯然是不合情理的咐低,因為Glide需要保證rootFragment的唯一性,rootFragment即fragment依附或者沒有fragment依附的activity所創(chuàng)建的最上層RequestManagerFragment袜腥。
接著往下看RequestManagerFragment的構造方法做了什么见擦。

public RequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
}

直接創(chuàng)建一個ActivityFragmentLifecycle,這個類實際是一個生命周期回調的管理類羹令,實現(xiàn)了Lifecycle接口鲤屡。所有的LifecycleListener會添加到一個集合中,當RequestManagerFragment生命周期方法觸發(fā)時福侈,會調用ActivityFragmentLifecycle相應生命周期方法酒来,這個方法然后再遍歷調用所有LifecycleListener的生命周期方法,以onStart生命周期方法為例肪凛,RequestManagerFragment中:

public void onStart() {
    super.onStart(); 
    lifecycle.onStart();
}

然后ActivityFragmentLifecycle中:

void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
        lifecycleListener.onStart();
    }
}

rootRequestManagerFragment

面UML圖上堰汉,可以知道RequestManagerFragment還有一個rootRequestManagerFragment的成員變量,Glide每創(chuàng)建一個RequestManagerFragment伟墙,都會嘗試實例化rootRequestManagerFragment翘鸭,這個fragment即頂級的Activity所創(chuàng)建的RequestManagerFragment,相關代碼

public void onAttach(Activity activity) {
    super.onAttach(activity);
    rootRequestManagerFragment = RequestManagerRetriever.get()
            .getRequestManagerFragment(getActivity().getFragmentManager());
    if (rootRequestManagerFragment != this) {
        rootRequestManagerFragment.addChildRequestManagerFragment(this);
    }
}

@Override
public void onDetach() {
    super.onDetach();
    if (rootRequestManagerFragment != null) {
        rootRequestManagerFragment.removeChildRequestManagerFragment(this);
        rootRequestManagerFragment = null;
    }
}

可以看到戳葵,不管當前的RequestManagerFragment是通過何種方式創(chuàng)建的就乓,都會在OnAttach時,拿到當前所綁定的Activity的FragmentManager來初始化一個RequestManagerFragment,這個RequestManagerFragment有可能是自身档址,有可能已經被初始化過了,比如是通過with(Activity activity)的方式初始化的邻梆,那么很顯然

RequestManagerRetriever.get().getRequestManagerFragment(getActivity().getFragmentManager());

這句代碼拿到的會是自己本身守伸,而如果是通過with(Fragment fragment)的形式創(chuàng)建的,rootRequestManagerFragment將指向當前fragment綁定到Activity所綁定的RequestManagerFragment浦妄,如果該Activity沒有綁定過尼摹,那么會開啟事務綁定一個RequestManagerFragment。并且如果自己不是rootRequestManagerFragment的話剂娄,那么將會把自己保存到rootRequestManagerFragment中的一個集合:

private void addChildRequestManagerFragment(RequestManagerFragment child) {
    childRequestManagerFragments.add(child);
}

簡而言之蠢涝,Glide會為Activity創(chuàng)建一個RequestManagerFragment做為rootFragment,并保存該Activity底下所有Fragment(如果有的話)所創(chuàng)建的RequestManagerFragment阅懦。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末和二,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耳胎,更是在濱河造成了極大的恐慌惯吕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怕午,死亡現(xiàn)場離奇詭異废登,居然都是意外死亡,警方通過查閱死者的電腦和手機郁惜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門堡距,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兆蕉,你說我怎么就攤上這事羽戒。” “怎么了恨樟?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵半醉,是天一觀的道長。 經常有香客問我劝术,道長缩多,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任养晋,我火速辦了婚禮衬吆,結果婚禮上,老公的妹妹穿的比我還像新娘绳泉。我一直安慰自己逊抡,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冒嫡,像睡著了一般拇勃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孝凌,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天方咆,我揣著相機與錄音,去河邊找鬼蟀架。 笑死瓣赂,一個胖子當著我的面吹牛,可吹牛的內容都是我干的片拍。 我是一名探鬼主播煌集,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捌省!你這毒婦竟也來了苫纤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤所禀,失蹤者是張志新(化名)和其女友劉穎方面,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體色徘,經...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡恭金,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了褂策。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片横腿。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斤寂,靈堂內的尸體忽然破棺而出耿焊,到底是詐尸還是另有隱情,我是刑警寧澤遍搞,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布罗侯,位于F島的核電站,受9級特大地震影響溪猿,放射性物質發(fā)生泄漏钩杰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一诊县、第九天 我趴在偏房一處隱蔽的房頂上張望讲弄。 院中可真熱鬧,春花似錦依痊、人聲如沸避除。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶摆。三九已至凉逛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間群井,已是汗流浹背鱼炒。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝌借,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓指蚁,卻偏偏與公主長得像菩佑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凝化,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內容