Android Glide图片加载框架(二)源码解析之with()
文章目錄
- 一、前言
- 二、如何閱讀源碼
- 三、源碼解析
- 1、with()
Android Glide圖片加載框架系列文章
Android Glide圖片加載框架(一)基本用法
Android Glide圖片加載框架(二)源碼解析之with()
Android Glide圖片加載框架(二)源碼解析之load()
Android Glide圖片加載框架(二)源碼解析之into()
Android Glide圖片加載框架(三)緩存機(jī)制
一、前言
在本系列的上一篇文章中,我們學(xué)習(xí)了Glide的基本用法,體驗(yàn)了這個(gè)圖片加載框架的強(qiáng)大功能,以及它非常簡(jiǎn)便的API。還沒(méi)有看過(guò)上一篇文章的朋友,建議先去閱讀 Android Glide圖片加載框架(一)基本用法。
在多數(shù)情況下,我們想要在界面上加載并展示一張圖片只需要一行代碼就能實(shí)現(xiàn),如下所示:
Glide.with(this).load(url).into(img);雖說(shuō)只有這簡(jiǎn)簡(jiǎn)單單的一行代碼,但大家可能不知道的是,Glide在背后幫我們默默執(zhí)行了成噸的工作。這個(gè)形容詞我想了很久,因?yàn)槲矣X(jué)得用非常多這個(gè)形容詞不足以描述Glide背后的工作量,我查到的英文資料是用tons of work來(lái)進(jìn)行形容的,因此我覺(jué)得這里使用成噸來(lái)形容更加貼切一些。
雖說(shuō)我們?cè)谄綍r(shí)使用Glide的時(shí)候格外地簡(jiǎn)單和方便,但是知其然也要知其所以然。那么今天我們就來(lái)解析一下Glide的源碼,看看它在這些簡(jiǎn)單用法的背后,到底執(zhí)行了多么復(fù)雜的工作。
二、如何閱讀源碼
在開始解析Glide源碼之前,我想先和大家談一下該如何閱讀源碼,這個(gè)問(wèn)題也是我平時(shí)被問(wèn)得比較多的,因?yàn)楹芏嗳硕加X(jué)得閱讀源碼是一件比較困難的事情。
那么閱讀源碼到底困難嗎?
這個(gè)當(dāng)然主要還是要視具體的源碼而定。比如同樣是圖片加載框架,我讀Volley的源碼時(shí)就感覺(jué)酣暢淋漓,并且對(duì)Volley的架構(gòu)設(shè)計(jì)和代碼質(zhì)量深感佩服。讀Glide的源碼時(shí)卻讓我相當(dāng)痛苦,代碼極其難懂。當(dāng)然這里我并不是說(shuō)Glide的代碼寫得不好,只是因?yàn)镚lide和復(fù)雜程度和Volley完全不是在一個(gè)量級(jí)上的。
那么,雖然源碼的復(fù)雜程度是外在的不可變條件,但我們卻可以通過(guò)一些技巧來(lái)提升自己閱讀源碼的能力。這里我和大家分享一下我平時(shí)閱讀源碼時(shí)所使用的技巧,簡(jiǎn)單概括就是八個(gè)字:抽絲剝繭、點(diǎn)到即止 。
應(yīng)該認(rèn)準(zhǔn)一個(gè)功能點(diǎn),然后去分析這個(gè)功能點(diǎn)是如何實(shí)現(xiàn)的。但只要去追尋主體的實(shí)現(xiàn)邏輯即可,千萬(wàn)不要試圖去搞懂每一行代碼都是什么意思,那樣很容易會(huì)陷入到思維黑洞當(dāng)中,而且越陷越深。因?yàn)檫@些龐大的系統(tǒng)都不是由一個(gè)人寫出來(lái)的,每一行代碼都想搞明白,就會(huì)感覺(jué)自己是在盲人摸象,永遠(yuǎn)也研究不透。如果只是去分析主體的實(shí)現(xiàn)邏輯,那么就有比較明確的目的性,這樣閱讀源碼會(huì)更加輕松,也更加有成效。
而今天帶大家閱讀的Glide源碼就非常適合使用這個(gè)技巧,因?yàn)镚lide的源碼太復(fù)雜了,千萬(wàn)不要試圖去搞明白它每行代碼的作用,而是應(yīng)該只分析它的主體實(shí)現(xiàn)邏輯。
那么我們本篇文章就先確立好一個(gè)目標(biāo),就是要通過(guò)閱讀源碼搞明白下面這行代碼:
Glide.with(this).load(url).into(img);到底是如何實(shí)現(xiàn)將一張網(wǎng)絡(luò)圖片展示到ImageView上面的。先將Glide的一整套圖片加載機(jī)制的基本流程梳理清楚,然后我們?cè)偻ㄟ^(guò)后面的幾篇文章具體去了解Glide源碼方方面面的細(xì)節(jié)。
準(zhǔn)備好了嗎?那么我們現(xiàn)在開始。
既然是要閱讀Glide的源碼,那么我們自然需要先將Glide的源碼下載下來(lái)。其實(shí)如果你是使用在 build.gradle 中添加依賴的方式將Glide引入到項(xiàng)目中的,那么源碼自動(dòng)就已經(jīng)下載下來(lái)了,在Android Studio中就可以直接進(jìn)行查看。
不過(guò),使用添加依賴的方式引入的Glide,我們只能看到它的源碼,但不能做任何的修改,如果你還需要修改它的源碼的話,可以到GitHub上面將它的完整源碼下載下來(lái)。
Glide的GitHub主頁(yè)的地址是:https://github.com/bumptech/glide
不過(guò)在這個(gè)地址下載到的永遠(yuǎn)都是最新的源碼,有可能還正在處于開發(fā)當(dāng)中。而我們整個(gè)系列都是使用Glide 4.8.0這個(gè)版本來(lái)進(jìn)行講解的,因此如果你需要專門去下載4.8.0版本的源碼,可以到這個(gè)地址進(jìn)行下載:https://github.com/bumptech/glide/tree/v4.8.0
三、源碼解析
1、with()
with() 方法是Glide類中的一組靜態(tài)方法,它有好幾個(gè)方法重載,我們來(lái)看一下Glide類中所有 with() 方法的方法重載:
public class Glide{...@NonNullpublic static RequestManager with(@NonNull Context context) {return getRetriever(context).get(context);}@NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull Fragment fragment) {return getRetriever(fragment.getActivity()).get(fragment);}@SuppressWarnings("deprecation")@Deprecated@NonNullpublic static RequestManager with(@NonNull android.app.Fragment fragment) {return getRetriever(fragment.getActivity()).get(fragment);}@NonNullpublic static RequestManager with(@NonNull View view) {return getRetriever(view.getContext()).get(view);}@NonNullprivate static RequestManagerRetriever getRetriever(@Nullable Context context) {// Context could be null for other reasons (ie the user passes in null), but in practice it will// only occur due to errors with the Fragment lifecycle.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 Glide.get(context).getRequestManagerRetriever();}... }可以看到,with() 方法的重載種類非常多,既可以傳入 Activity ,也可以傳入 Fragment 或者是 Context 。每一個(gè) with() 方法重載的代碼都非常簡(jiǎn)單,都是先調(diào)用 getRetriever() 方法獲取 RequestManagerRetriever 對(duì)象,然后再調(diào)用 RequestManagerRetriever 的實(shí)例 get() 方法,去獲取 RequestManager 對(duì)象。
而 RequestManagerRetriever 的實(shí)例 get() 方法中的邏輯是什么樣的呢?我們一起來(lái)看一看:
public class RequestManagerRetriever implements Handler.Callback {/*** The top application level RequestManager.*/private volatile RequestManager applicationManager;@NonNullprivate RequestManager getApplicationManager(@NonNull Context context) {// Either an application context or we're on a background thread.if (applicationManager == null) {synchronized (this) {if (applicationManager == null) {// Normally pause/resume is taken care of by the fragment we add to the fragment or// activity. However, in this case since the manager attached to the application will not// receive lifecycle events, we must force the manager to start resumed using// ApplicationLifecycle.// TODO(b/27524013): Factor out this Glide.get() call.Glide glide = Glide.get(context.getApplicationContext());applicationManager =factory.build(glide,new ApplicationLifecycle(),new EmptyRequestManagerTreeNode(),context.getApplicationContext());}}}return applicationManager;}@NonNullpublic 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);}@NonNullpublic 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));}}@NonNullpublic 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());}}@SuppressWarnings("deprecation")@NonNullpublic RequestManager get(@NonNull Activity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);android.app.FragmentManager fm = activity.getFragmentManager();return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}@SuppressWarnings("deprecation")@NonNullpublic RequestManager get(@NonNull View view) {if (Util.isOnBackgroundThread()) {return get(view.getContext().getApplicationContext());}Preconditions.checkNotNull(view);Preconditions.checkNotNull(view.getContext(),"Unable to obtain a request manager for a view without a Context");Activity activity = findActivity(view.getContext());// The view might be somewhere else, like a service.if (activity == null) {return get(view.getContext().getApplicationContext());}// Support Fragments.// Although the user might have non-support Fragments attached to FragmentActivity, searching// for non-support Fragments is so expensive pre O and that should be rare enough that we// prefer to just fall back to the Activity directly.if (activity instanceof FragmentActivity) {Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);return fragment != null ? get(fragment) : get(activity);}// Standard Fragments.android.app.Fragment fragment = findFragment(view, activity);if (fragment == null) {return get(activity);}return get(fragment);}@SuppressWarnings("deprecation")@Deprecated@NonNull@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)public RequestManager get(@NonNull 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, fragment, fragment.isVisible());}}@SuppressWarnings("deprecation")@Deprecated@NonNullRequestManagerFragment getRequestManagerFragment(Activity activity) {return getRequestManagerFragment(activity.getFragmentManager(), /*parentHint=*/ null, isActivityVisible(activity));}@SuppressWarnings("deprecation")@NonNullprivate RequestManagerFragment getRequestManagerFragment(@NonNull final android.app.FragmentManager fm,@Nullable android.app.Fragment parentHint,boolean isParentVisible) {RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingRequestManagerFragments.get(fm);if (current == null) {current = new RequestManagerFragment();current.setParentFragmentHint(parentHint);if (isParentVisible) {current.getGlideLifecycle().onStart();}pendingRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})@Deprecated@NonNullprivate RequestManager fragmentGet(@NonNull Context context,@NonNull android.app.FragmentManager fm,@Nullable android.app.Fragment parentHint,boolean isParentVisible) {RequestManagerFragment current = getRequestManagerFragment(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;}@NonNullprivate SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {SupportRequestManagerFragment current =(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingSupportRequestManagerFragments.get(fm);if (current == null) {current = new SupportRequestManagerFragment();current.setParentFragmentHint(parentHint);if (isParentVisible) {current.getGlideLifecycle().onStart();}pendingSupportRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}@NonNullprivate 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;} }上述代碼雖然看上去邏輯有點(diǎn)復(fù)雜,但是將它們梳理清楚后還是很簡(jiǎn)單的。 RequestManagerRetriever 類中看似有很多個(gè) get() 方法的重載,什么Context參數(shù),Activity參數(shù),Fragment參數(shù)等等,實(shí)際上只有兩種情況而已,即傳入Application類型的參數(shù),和傳入非Application類型的參數(shù) 。
-
Application類型參數(shù): 如果在 Glide.with() 方法中傳入的是一個(gè) Application對(duì)象 ,那么這里就會(huì)調(diào)用帶有Context參數(shù)的get()方法重載,然后會(huì)在第48行調(diào)用 getApplicationManager() 方法來(lái)獲取一個(gè) RequestManager 對(duì)象。其實(shí)這是最簡(jiǎn)單的一種情況,因?yàn)锳pplication對(duì)象的生命周期即應(yīng)用程序的生命周期,因此Glide并不需要做什么特殊的處理,它自動(dòng)就是和應(yīng)用程序的生命周期是同步的,如果應(yīng)用程序關(guān)閉的話,Glide的加載也會(huì)同時(shí)終止。
-
非Application類型參數(shù): 不管你在 Glide.with() 方法中傳入的是Activity、FragmentActivity、v4包下的Fragment、還是app包下的Fragment,最終的流程都是一樣的,那就是 會(huì)向當(dāng)前的Activity當(dāng)中添加一個(gè)隱藏的Fragment 。具體添加的邏輯是在上述代碼的第176行和第215行,分別對(duì)應(yīng)的app包和v4包下的兩種Fragment的情況。
這里為什么要添加一個(gè)隱藏的Fragment呢?
因?yàn)镚lide需要知道加載的生命周期。很簡(jiǎn)單的一個(gè)道理,如果你在某個(gè)Activity上正在加載著一張圖片,結(jié)果圖片還沒(méi)加載出來(lái),Activity就被用戶關(guān)掉了,那么圖片還應(yīng)該繼續(xù)加載嗎?當(dāng)然不應(yīng)該。可是 Glide并沒(méi)有辦法知道Activity的生命周期,于是Glide就使用了添加隱藏Fragment的這種小技巧,因?yàn)镕ragment的生命周期和Activity是同步的,如果Activity被銷毀了,Fragment是可以監(jiān)聽(tīng)到的,這樣Glide就可以捕獲這個(gè)事件并停止圖片加載了。
這里額外再提一句,從第54行代碼可以看出,如果我們是在非主線程當(dāng)中使用的Glide,那么不管你是傳入的Activity還是Fragment,都會(huì)被強(qiáng)制當(dāng)成Application來(lái)處理 。不過(guò)其實(shí)這就屬于是在分析代碼的細(xì)節(jié)了,本篇文章我們將會(huì)把目光主要放在Glide的主線工作流程上面,后面不會(huì)過(guò)多去分析這些細(xì)節(jié)方面的內(nèi)容。
總體來(lái)說(shuō),第一個(gè)with()方法的源碼還是比較好理解的。其實(shí)就是為了得到一個(gè)RequestManager對(duì)象而已,然后 Glide會(huì)根據(jù)我們傳入with()方法的參數(shù)來(lái)確定圖片加載的生命周期 ,并沒(méi)有什么特別復(fù)雜的邏輯。不過(guò)復(fù)雜的邏輯還在后面等著我們呢,接下來(lái)我們開始分析第二步,load()方法。
總結(jié)
以上是生活随笔為你收集整理的Android Glide图片加载框架(二)源码解析之with()的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c++面向对象高级编程 学习十 func
- 下一篇: Android设计模式之——工厂方法模式