〔兩行哥〕提綱挈領(lǐng)捧韵,帶你梳理Glide主要源碼邏輯(一)

Glide的簡介就不多說了,只要記住這是Google推薦AndroidDev使用的圖片開源框架就好了秤掌。直接進(jìn)入正題愁铺,看看Glide的設(shè)計到底有何精妙之處,為何獲得了Google的青睞闻鉴?

一個最簡單的Glide圖片加載邏輯舉例:

Glide.with(context).load(url).into(imageView);

我們的分析會以Glide3.8.0版本為例茵乱,基于此行代碼展開,分別分析with()孟岛、load()瓶竭、into()三個方法內(nèi)部的邏輯督勺。

一、基礎(chǔ)概念

Glide中涉及幾個概念斤贰,為便于理解智哀,先粗略解釋:

1.Model

指的是對圖片源的封裝。在AndroidDev中荧恍,比較常見的就是網(wǎng)絡(luò)圖片地址瓷叫、本地文件或資源ID。比如块饺,String url = "https://www.baidu.com/img/bd_logo1.png"赞辩,這里的url經(jīng)過Glide內(nèi)部的封裝后,就可以理解為一個Model授艰。

2.Data

指的是對Model處理后的數(shù)據(jù)源的封裝辨嗽,通常是InputStream。而在Glide中淮腾,將Model處理為Data的類便是ModelLoader糟需。比如,通過上文的"https://www.baidu.com/img/bd_logo1.png"而取得的圖片輸入流谷朝,就可以理解為Data洲押。

3.Resource

拿到圖片的輸入流可以直接展示在UI上嗎?顯然不可以圆凰。在第2點中我們提及了Data杈帐,即輸入流,如果要將其展示就需要對Data進(jìn)行解碼专钉,解碼后的數(shù)據(jù)就是Resource挑童。比如,上文的圖片輸入流經(jīng)過解碼后成為一個Bitmap對象或Drawable對象跃须,這個對象就可以理解為Resource站叼。而擔(dān)任解碼任務(wù)的角色,被成為ResourceDecoder(資源解碼器)菇民。

4.TransformedResource&TranscodedResource

有時候我們獲取到的Resource并不適合展示尽楔,而是需要經(jīng)過處理才能展示,比如需要裁剪變換等(如調(diào)用centerCrop()第练、fitCenter())阔馋,那么經(jīng)過如此變換后的Rescourse稱為TransformedResource。
我們知道Glide是可以展示靜態(tài)圖娇掏,也可以展示動態(tài)圖(動態(tài)Gif)垦缅,而解碼后的靜態(tài)圖片和動態(tài)圖片(例如drawable和gif-drawable)類型是不同的, 為了統(tǒng)一處理邏輯驹碍,Glide內(nèi)部將這兩種類型的對象再次封裝為統(tǒng)一的GlideBitmapDrawable壁涎,這里的GlideBitmapDrawable就可以理解為TranscodedResource凡恍。

5.Target

這個比較容易理解,即需要在哪個目標(biāo)上進(jìn)行展示怔球,比如ImageView嚼酝。而Glide內(nèi)部將ImageView再次進(jìn)行了封裝,封裝后對象就可以理解為Target竟坛。

綜上所述闽巩,一個完整的圖片處理及展示流程如下:


圖1 Glide流程圖

二、基本用法

直接看代碼和注釋:

Glide.with(context)
        .load(url)
        .placeholder(R.mipmap.ic_launcher)//圖片加載前的占位圖
        .error(R.mipmap.ic_launcher)//圖片加載錯誤的占位圖
        .fitCenter()
        .centerCrop()
        .override(500, 500)//調(diào)整圖片大小
        .skipMemoryCache(true)//跳過內(nèi)存緩存
        .crossFade(1000)//漸變顯示時間
        .diskCacheStrategy(DiskCacheStrategy.RESULT)//緩存處理后的圖像(如尺寸調(diào)整担汤、裁剪后的圖像)
        .diskCacheStrategy(DiskCacheStrategy.SOURCE)//緩存原尺寸的圖像
        .diskCacheStrategy(DiskCacheStrategy.ALL)//緩存所有圖像
        .diskCacheStrategy(DiskCacheStrategy.NONE)//跳過磁盤緩存
        .priority(Priority.HIGH)//指定優(yōu)先級
        .into(imageView);

三涎跨、with()源碼邏輯

跟進(jìn)Glide類中尋找with()方法,發(fā)現(xiàn)with()方法有多種重載崭歧,包括with(Context context)隅很、with(Activity activity)、with(Fragment fragment)等等率碾。Glide之所以設(shè)計如此之多的with()方法重載叔营,其目的在于將圖片的加載與傳入的Context組件或Fragment的生命周期相關(guān)聯(lián)。比如所宰,當(dāng)用戶關(guān)閉當(dāng)前Activity绒尊,那么即使該Activity有正在進(jìn)行中的圖片網(wǎng)絡(luò)請求,Glide也會隨之取消該網(wǎng)絡(luò)請求仔粥。如果傳入的是ApplicationContext婴谱,那么網(wǎng)絡(luò)請求會持續(xù)進(jìn)行,直至App進(jìn)程被銷毀躯泰。
這里以with(Context context)為例看一下源碼實現(xiàn)谭羔,其他重載方法邏輯基本一致:

Glide.java
    public static RequestManager with(Context  context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

RequestManager類是Glide中用來處理圖片加載請求的管理類。RequestManagerRetriever類是用來創(chuàng)建和管理RequestManager對象的斟冕,其get()方法獲取了RequestManagerRetriever實例。繼續(xù)追蹤源碼缅阳,查看RequestManagerRetriever類中g(shù)et()方法的邏輯磕蛇。

RequestManagerRetriever.java
    public RequestManager get(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);
    }

在get()方法中,對Context的類型進(jìn)行了區(qū)分十办,有多種重載秀撇。我們主要分析一下Application和Activity類型的方法參數(shù),其他類型的參數(shù)讀者可以自行追蹤源碼向族。

RequestManagerRetriever.java
    private RequestManager getApplicationManager(Context context) {
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }
        return applicationManager;
    }

上述get(Application context)的方法中采取了雙重鎖單例模式獲取了一個RequestManager對象呵燕。可以發(fā)現(xiàn)件相,Glide在RequestManager的構(gòu)造方法中傳入了ApplicationLifecycle的實例再扭,其作用是將RequestManager與Application生命周期綁定氧苍。

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    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);
        }
    }

上述get(Activity context)的方法中,首先進(jìn)行了線程判斷泛范,如果在非主線程的情況下让虐,返回了get(Application context)類型的RequestManager,而在主線程的情況下罢荡,通過該Activity context獲取到了相關(guān)聯(lián)的FragmentManager赡突。這是Glide設(shè)計精妙之處之一,為什么要獲取FragmentManger呢区赵?繼續(xù)追蹤源碼到fragmentGet(activity惭缰,fm)中。

RequestManagerRetriever.java
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

分析一下fragmentGet()的邏輯笼才。首先漱受,方法第一行g(shù)etRequestManagerFragment()返回了RequestManagerFragment的實例current。RequestManagerFragment是啥患整?其實這是Glide封裝的Fragment拜效,這里當(dāng)作Fragment來理解。先來截取部分RequestManagerFragment類的代碼:

RequestManagerFragment.java
public class RequestManagerFragment extends Fragment {

    ......
    private final ActivityFragmentLifecycle lifecycle;
    private RequestManager requestManager;

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

    /**
     * Sets the current {@link com.bumptech.glide.RequestManager}.
     *
     * @param requestManager The request manager to use.
     */
    public void setRequestManager(RequestManager requestManager) {
        this.requestManager = requestManager;
    }

    ActivityFragmentLifecycle getLifecycle() {
        return lifecycle;
    }

    /**
     * Returns the current {@link com.bumptech.glide.RequestManager} or null if none exists.
     */
    public RequestManager getRequestManager() {
        return requestManager;
    }

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

    @Override
    public void onStop() {
        super.onStop();
        lifecycle.onStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        lifecycle.onDestroy();
    }

}

對RequestManagerFragment劃重點:
1.含有一個RequestManager類型的成員變量requestManager各谚,通過getRequestManager()返回紧憾,通過setRequestManager()賦值。

即每個RequestManagerFragment對象都與一個RequestManager對象綁定昌渤。

2.含有一個ActivityFragmentLifecycle類型的成員變量lifecycle赴穗。通過對lifecycle實現(xiàn)的LifecycleListener接口回調(diào)來監(jiān)聽其生命周期,可以發(fā)現(xiàn)在RequestManagerFragment類的onStart()膀息、onStop()般眉、onDestroy()方法中對其進(jìn)行了調(diào)用。

即lifecycle對象實現(xiàn)了對RequestManagerFragment實例的生命周期監(jiān)聽潜支。

回到fragmentGet()方法中甸赃,調(diào)用RequestManagerFragment中的getRequestManager()方法返回與其綁定的RequestManager對象(成員變量),如果為null(沒有與其綁定的RequestManager對象)冗酿,則創(chuàng)建一個RequestManager對象埠对,并調(diào)用setRequestManager()將其與RequestManagerFragment綁定。至此實現(xiàn)了RequestManagerFragment對象與RequestManager對象的一一綁定裁替。
現(xiàn)在思考一個問題项玛,Glide如何監(jiān)聽目標(biāo)Activity或Fragment的生命周期?上文已經(jīng)說明弱判,根據(jù)傳入Glide的Context不同襟沮,Glide會監(jiān)聽其生命周期,根據(jù)生命周期管理圖片的網(wǎng)絡(luò)請求。
Glide直接監(jiān)聽Activity并不方便开伏。那么在這里膀跌,Glide采用了另外的方法:
1.在Activity或Fragment的上層添再加一個Fragment,也就是上文一直在分析的RequestManagerFragment硅则。這個Fragment并沒有覆寫createFragmentView()淹父,即這是一個無UI的Fragment,當(dāng)添加了該Fragment怎虫,用戶是無法感知到的暑认。
2.我們知道依附于Activity的Fragment有與被依附的Activity相關(guān)聯(lián)的生命周期,監(jiān)聽Fragment的生命周期也就是獲取到了被依附的Activity的生命周期大审。上文已經(jīng)劃過重點:RequestManagerFragment通過接口回調(diào)的方式蘸际,在onStart()、onStop()徒扶、onDestroy()中調(diào)用了ActivityFragmentLifecycle的相應(yīng)生命周期方法粮彤,實現(xiàn)了對其生命周期的監(jiān)聽。
3.如果傳入Glide.with()參數(shù)為Fragment姜骡,那么處理邏輯同上导坟。大家應(yīng)該也在Fragment中添加過Fragment吧?Glide同樣監(jiān)聽了RequestManagerFragment的生命周期圈澈。
4.每個RequestManagerFragment對象又與RequestManager對象一一綁定惫周。至此,所有疑問解開:

Glide的每個RequestManager想要監(jiān)聽目標(biāo)Activity或Fragment生命周期用于執(zhí)行相關(guān)操作:
---> 每個RequestManager關(guān)聯(lián)著唯一的RequestManagerFragment
---> 該RequestManagerFragment是無UI的康栈,被添加在目標(biāo)Activity或Fragment上递递, 而RequestManagerFragment的生命周期又與目標(biāo)Activity或Fragment相關(guān)聯(lián)
---> RequestManagerFragment通過接口回調(diào)的方式,在onStart()啥么、onStop()登舞、onDestroy()中調(diào)用了ActivityFragmentLifecycle的相應(yīng)生命周期方法,實現(xiàn)了生命周期的監(jiān)聽
綜上悬荣,RequestManager通過監(jiān)聽RequestManagerFragment這個不可見的Fragment的生命周期菠秒,間接監(jiān)聽了目標(biāo)Activity或Fragment。

這次的Glide源碼分析就到這里氯迂,主要分析了Glide內(nèi)部的處理流程以及with()方法中所做的操作践叠,建議自行參閱相關(guān)源碼的同時閱讀本文。下一篇我們將繼續(xù)分析Glide的load()方法囚戚,再會酵熙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轧简,一起剝皮案震驚了整個濱河市驰坊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哮独,老刑警劉巖拳芙,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察藐,死亡現(xiàn)場離奇詭異,居然都是意外死亡舟扎,警方通過查閱死者的電腦和手機分飞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睹限,“玉大人譬猫,你說我怎么就攤上這事∠哿疲” “怎么了染服?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叨恨。 經(jīng)常有香客問我柳刮,道長,這世上最難降的妖魔是什么痒钝? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任秉颗,我火速辦了婚禮,結(jié)果婚禮上送矩,老公的妹妹穿的比我還像新娘蚕甥。我一直安慰自己,他們只是感情好益愈,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布梢灭。 她就那樣靜靜地躺著,像睡著了一般蒸其。 火紅的嫁衣襯著肌膚如雪敏释。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天摸袁,我揣著相機與錄音钥顽,去河邊找鬼。 笑死靠汁,一個胖子當(dāng)著我的面吹牛蜂大,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝶怔,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼奶浦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了踢星?” 一聲冷哼從身側(cè)響起澳叉,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后成洗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體五督,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年瓶殃,在試婚紗的時候發(fā)現(xiàn)自己被綠了充包。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡遥椿,死狀恐怖基矮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冠场,我是刑警寧澤愈捅,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站慈鸠,受9級特大地震影響蓝谨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜青团,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一譬巫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧督笆,春花似錦芦昔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至料扰,卻和暖如春凭豪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晒杈。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工嫂伞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拯钻。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓帖努,卻偏偏與公主長得像,于是被迫代替她去往敵國和親粪般。 傳聞我的和親對象是個殘疾皇子拼余,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容