原文鏈接
在本系列的上一篇文章中,我們學(xué)習(xí)了Glide的基本用法报账,體驗(yàn)了這個圖片加載框架的強(qiáng)大功能研底,以及它非常簡便的API。還沒有看過上一篇文章的朋友透罢,建議先去閱讀 Android圖片加載框架最全解析(一)榜晦,Glide的基本用法 。
在多數(shù)情況下羽圃,我們想要在界面上加載并展示一張圖片只需要一行代碼就能實(shí)現(xiàn)乾胶,如下所示:
Glide.with(this).load(url).into(imageView);
1
雖說只有這簡簡單單的一行代碼,但大家可能不知道的是统屈,Glide在背后幫我們默默執(zhí)行了成噸的工作胚吁。這個形容詞我想了很久,因?yàn)槲矣X得用非常多這個形容詞不足以描述Glide背后的工作量愁憔,我查到的英文資料是用tons of work來進(jìn)行形容的腕扶,因此我覺得這里使用成噸來形容更加貼切一些。
雖說我們在平時使用Glide的時候格外地簡單和方便吨掌,但是知其然也要知其所以然半抱。那么今天我們就來解析一下Glide的源碼,看看它在這些簡單用法的背后膜宋,到底執(zhí)行了多么復(fù)雜的工作窿侈。
如何閱讀源碼
在開始解析Glide源碼之前,我想先和大家談一下該如何閱讀源碼秋茫,這個問題也是我平時被問得比較多的史简,因?yàn)楹芏嗳硕加X得閱讀源碼是一件比較困難的事情。
那么閱讀源碼到底困難嗎肛著?這個當(dāng)然主要還是要視具體的源碼而定圆兵。比如同樣是圖片加載框架,我讀Volley的源碼時就感覺酣暢淋漓枢贿,并且對Volley的架構(gòu)設(shè)計和代碼質(zhì)量深感佩服殉农。讀Glide的源碼時卻讓我相當(dāng)痛苦,代碼極其難懂局荚。當(dāng)然這里我并不是說Glide的代碼寫得不好超凳,只是因?yàn)镚lide和復(fù)雜程度和Volley完全不是在一個量級上的愈污。
那么,雖然源碼的復(fù)雜程度是外在的不可變條件轮傍,但我們卻可以通過一些技巧來提升自己閱讀源碼的能力暂雹。這里我和大家分享一下我平時閱讀源碼時所使用的技巧,簡單概括就是八個字:抽絲剝繭金麸、點(diǎn)到即止擎析。應(yīng)該認(rèn)準(zhǔn)一個功能點(diǎn),然后去分析這個功能點(diǎn)是如何實(shí)現(xiàn)的挥下。但只要去追尋主體的實(shí)現(xiàn)邏輯即可揍魂,千萬不要試圖去搞懂每一行代碼都是什么意思否过,那樣很容易會陷入到思維黑洞當(dāng)中侦讨,而且越陷越深需纳。因?yàn)檫@些龐大的系統(tǒng)都不是由一個人寫出來的皮钠,每一行代碼都想搞明白娩梨,就會感覺自己是在盲人摸象帚称,永遠(yuǎn)也研究不透杰妓。如果只是去分析主體的實(shí)現(xiàn)邏輯卓箫,那么就有比較明確的目的性迷雪,這樣閱讀源碼會更加輕松限书,也更加有成效。
而今天帶大家閱讀的Glide源碼就非常適合使用這個技巧章咧,因?yàn)镚lide的源碼太復(fù)雜了倦西,千萬不要試圖去搞明白它每行代碼的作用,而是應(yīng)該只分析它的主體實(shí)現(xiàn)邏輯赁严。那么我們本篇文章就先確立好一個目標(biāo)扰柠,就是要通過閱讀源碼搞明白下面這行代碼:
Glide.with(this).load(url).into(imageView);
1
到底是如何實(shí)現(xiàn)將一張網(wǎng)絡(luò)圖片展示到ImageView上面的。先將Glide的一整套圖片加載機(jī)制的基本流程梳理清楚疼约,然后我們再通過后面的幾篇文章具體去了解Glide源碼方方面面的細(xì)節(jié)卤档。
準(zhǔn)備好了嗎?那么我們現(xiàn)在開始程剥。
源碼下載
既然是要閱讀Glide的源碼劝枣,那么我們自然需要先將Glide的源碼下載下來。其實(shí)如果你是使用在build.gradle中添加依賴的方式將Glide引入到項(xiàng)目中的织鲸,那么源碼自動就已經(jīng)下載下來了哨免,在Android Studio中就可以直接進(jìn)行查看。
不過昙沦,使用添加依賴的方式引入的Glide,我們只能看到它的源碼载荔,但不能做任何的修改盾饮,如果你還需要修改它的源碼的話,可以到GitHub上面將它的完整源碼下載下來。
Glide的GitHub主頁的地址是:https://github.com/bumptech/glide
不過在這個地址下載到的永遠(yuǎn)都是最新的源碼丘损,有可能還正在處于開發(fā)當(dāng)中普办。而我們整個系列都是使用Glide 3.7.0這個版本來進(jìn)行講解的,因此如果你需要專門去下載3.7.0版本的源碼徘钥,可以到這個地址進(jìn)行下載:https://github.com/bumptech/glide/tree/v3.7.0
開始閱讀
我們在上一篇文章中已經(jīng)學(xué)習(xí)過了衔蹲,Glide最基本的用法就是三步走:先with(),再load()呈础,最后into()舆驶。那么我們開始一步步閱讀這三步走的源碼,先從with()看起而钞。
- with()
with()方法是Glide類中的一組靜態(tài)方法沙廉,它有好幾個方法重載,我們來看一下Glide類中所有with()方法的方法重載:
public class Glide {
...
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
}
可以看到臼节,with()方法的重載種類非常多撬陵,既可以傳入Activity,也可以傳入Fragment或者是Context网缝。每一個with()方法重載的代碼都非常簡單巨税,都是先調(diào)用RequestManagerRetriever的靜態(tài)get()方法得到一個RequestManagerRetriever對象,這個靜態(tài)get()方法就是一個單例實(shí)現(xiàn)粉臊,沒什么需要解釋的草添。然后再調(diào)用RequestManagerRetriever的實(shí)例get()方法,去獲取RequestManager對象维费。
而RequestManagerRetriever的實(shí)例get()方法中的邏輯是什么樣的呢果元?我們一起來看一看:
public class RequestManagerRetriever implements Handler.Callback {
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
private volatile RequestManager applicationManager;
...
/**
* Retrieves and returns the RequestManagerRetriever singleton.
*/
public static RequestManagerRetriever get() {
return INSTANCE;
}
private RequestManager getApplicationManager(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.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
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);
}
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@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);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
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);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
@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;
}
SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
...
}
上述代碼雖然看上去邏輯有點(diǎn)復(fù)雜,但是將它們梳理清楚后還是很簡單的犀盟。RequestManagerRetriever類中看似有很多個get()方法的重載而晒,什么Context參數(shù),Activity參數(shù)阅畴,F(xiàn)ragment參數(shù)等等倡怎,實(shí)際上只有兩種情況而已,即傳入Application類型的參數(shù)贱枣,和傳入非Application類型的參數(shù)监署。
我們先來看傳入Application參數(shù)的情況。如果在Glide.with()方法中傳入的是一個Application對象纽哥,那么這里就會調(diào)用帶有Context參數(shù)的get()方法重載钠乏,然后會在第44行調(diào)用getApplicationManager()方法來獲取一個RequestManager對象。其實(shí)這是最簡單的一種情況春塌,因?yàn)锳pplication對象的生命周期即應(yīng)用程序的生命周期晓避,因此Glide并不需要做什么特殊的處理簇捍,它自動就是和應(yīng)用程序的生命周期是同步的,如果應(yīng)用程序關(guān)閉的話俏拱,Glide的加載也會同時終止暑塑。
接下來我們看傳入非Application參數(shù)的情況。不管你在Glide.with()方法中傳入的是Activity锅必、FragmentActivity事格、v4包下的Fragment、還是app包下的Fragment搞隐,最終的流程都是一樣的驹愚,那就是會向當(dāng)前的Activity當(dāng)中添加一個隱藏的Fragment。具體添加的邏輯是在上述代碼的第117行和第141行尔许,分別對應(yīng)的app包和v4包下的兩種Fragment的情況么鹤。那么這里為什么要添加一個隱藏的Fragment呢?因?yàn)镚lide需要知道加載的生命周期味廊。很簡單的一個道理蒸甜,如果你在某個Activity上正在加載著一張圖片,結(jié)果圖片還沒加載出來余佛,Activity就被用戶關(guān)掉了柠新,那么圖片還應(yīng)該繼續(xù)加載嗎?當(dāng)然不應(yīng)該辉巡『拊鳎可是Glide并沒有辦法知道Activity的生命周期,于是Glide就使用了添加隱藏Fragment的這種小技巧郊楣,因?yàn)镕ragment的生命周期和Activity是同步的憔恳,如果Activity被銷毀了,F(xiàn)ragment是可以監(jiān)聽到的净蚤,這樣Glide就可以捕獲這個事件并停止圖片加載了钥组。
這里額外再提一句,從第48行代碼可以看出今瀑,如果我們是在非主線程當(dāng)中使用的Glide程梦,那么不管你是傳入的Activity還是Fragment,都會被強(qiáng)制當(dāng)成Application來處理橘荠。不過其實(shí)這就屬于是在分析代碼的細(xì)節(jié)了屿附,本篇文章我們將會把目光主要放在Glide的主線工作流程上面,后面不會過多去分析這些細(xì)節(jié)方面的內(nèi)容哥童。
總體來說挺份,第一個with()方法的源碼還是比較好理解的。其實(shí)就是為了得到一個RequestManager對象而已贮懈,然后Glide會根據(jù)我們傳入with()方法的參數(shù)來確定圖片加載的生命周期匀泊,并沒有什么特別復(fù)雜的邏輯影暴。不過復(fù)雜的邏輯還在后面等著我們呢,接下來我們開始分析第二步探赫,load()方法。
- load()
由于with()方法返回的是一個RequestManager對象撬呢,那么很容易就能想到伦吠,load()方法是在RequestManager類當(dāng)中的,所以說我們首先要看的就是RequestManager這個類魂拦。不過在上一篇文章中我們學(xué)過毛仪,Glide是支持圖片URL字符串、圖片本地路徑等等加載形式的芯勘,因此RequestManager中也有很多個load()方法的重載箱靴。但是這里我們不可能把每個load()方法的重載都看一遍,因此我們就只選其中一個加載圖片URL字符串的load()方法來進(jìn)行研究吧荷愕。
RequestManager類的簡化代碼如下所示:
public class RequestManager implements LifecycleListener {
...
/**
* Returns a request builder to load the given {@link String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
/**
* Returns a request builder that loads data from {@link String}s using an empty signature.
*
* <p>
* Note - this method caches data using only the given String as the cache key. If the data is a Uri outside of
* your control, or you otherwise expect the data represented by the given String to change without the String
* identifier changing, Consider using
* {@link GenericRequestBuilder#signature(Key)} to mixin a signature
* you create that identifies the data currently at the given String that will invalidate the cache if that data
* changes. Alternatively, using {@link DiskCacheStrategy#NONE} and/or
* {@link DrawableRequestBuilder#skipMemoryCache(boolean)} may be appropriate.
* </p>
*
* @see #from(Class)
* @see #load(String)
*/
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
...
}
RequestManager類的代碼是非常多的衡怀,但是經(jīng)過我這樣簡化之后,看上去就比較清爽了安疗。在我們只探究加載圖片URL字符串這一個load()方法的情況下抛杨,那么比較重要的方法就只剩下上述代碼中的這三個方法。
那么我們先來看load()方法荐类,這個方法中的邏輯是非常簡單的怖现,只有一行代碼,就是先調(diào)用了fromString()方法玉罐,再調(diào)用load()方法屈嗤,然后把傳入的圖片URL地址傳進(jìn)去。而fromString()方法也極為簡單吊输,就是調(diào)用了loadGeneric()方法饶号,并且指定參數(shù)為String.class,因?yàn)閘oad()方法傳入的是一個字符串參數(shù)璧亚。那么看上去讨韭,好像主要的工作都是在loadGeneric()方法中進(jìn)行的了。
其實(shí)loadGeneric()方法也沒幾行代碼癣蟋,這里分別調(diào)用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法來獲得ModelLoader對象透硝。ModelLoader對象是用于加載圖片的,而我們給load()方法傳入不同類型的參數(shù)疯搅,這里也會得到不同的ModelLoader對象濒生。不過buildStreamModelLoader()方法內(nèi)部的邏輯還是蠻復(fù)雜的,這里就不展開介紹了幔欧,要不然篇幅實(shí)在收不住罪治,感興趣的話你可以自己研究丽声。由于我們剛才傳入的參數(shù)是String.class,因此最終得到的是StreamStringLoader對象觉义,它是實(shí)現(xiàn)了ModelLoader接口的雁社。
最后我們可以看到,loadGeneric()方法是要返回一個DrawableTypeRequest對象的晒骇,因此在loadGeneric()方法的最后又去new了一個DrawableTypeRequest對象霉撵,然后把剛才獲得的ModelLoader對象,還有一大堆雜七雜八的東西都傳了進(jìn)去洪囤。具體每個參數(shù)的含義和作用就不解釋了徒坡,我們只看主線流程。
那么這個DrawableTypeRequest的作用是什么呢瘤缩?我們來看下它的源碼喇完,如下所示:
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
/**
* Attempts to always load the resource as a {@link android.graphics.Bitmap}, even if it could actually be animated.
*
* @return A new request builder for loading a {@link android.graphics.Bitmap}
*/
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
/**
* Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
* <p>
* If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
* represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
* just an {@link DrawableTypeRequest} is sufficient because it will determine whether or
* not the given data represents an animated GIF and return the appropriate animated or not animated
* {@link android.graphics.drawable.Drawable} automatically.
* </p>
*
* @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}
...
}
這個類中的代碼本身就不多,我只是稍微做了一點(diǎn)簡化剥啤〗跸可以看到,最主要的就是它提供了asBitmap()和asGif()這兩個方法铐殃。這兩個方法我們在上一篇文章當(dāng)中都是學(xué)過的海洼,分別是用于強(qiáng)制指定加載靜態(tài)圖片和動態(tài)圖片。而從源碼中可以看出富腊,它們分別又創(chuàng)建了一個BitmapTypeRequest和GifTypeRequest坏逢,如果沒有進(jìn)行強(qiáng)制指定的話,那默認(rèn)就是使用DrawableTypeRequest赘被。
好的是整,那么我們再回到RequestManager的load()方法中。剛才已經(jīng)分析過了民假,fromString()方法會返回一個DrawableTypeRequest對象浮入,接下來會調(diào)用這個對象的load()方法,把圖片的URL地址傳進(jìn)去羊异。但是我們剛才看到了事秀,DrawableTypeRequest中并沒有l(wèi)oad()方法,那么很容易就能猜想到野舶,load()方法是在父類當(dāng)中的易迹。
DrawableTypeRequest的父類是DrawableRequestBuilder,我們來看下這個類的源碼:
public class DrawableRequestBuilder<ModelType>
extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
implements BitmapOptions, DrawableOptions {
DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}
public DrawableRequestBuilder<ModelType> thumbnail(
DrawableRequestBuilder<?> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> thumbnail(
GenericRequestBuilder<?, ?, ?, GlideDrawable> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
super.decoder(decoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
super.encoder(encoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> priority(Priority priority) {
super.priority(priority);
return this;
}
public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
public DrawableRequestBuilder<ModelType> fitCenter() {
return transform(glide.getDrawableFitCenter());
}
public DrawableRequestBuilder<ModelType> bitmapTransform(Transformation<Bitmap>... bitmapTransformations) {
GifBitmapWrapperTransformation[] transformations =
new GifBitmapWrapperTransformation[bitmapTransformations.length];
for (int i = 0; i < bitmapTransformations.length; i++) {
transformations[i] = new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);
}
return transform(transformations);
}
@Override
public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
super.transform(transformation);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> transcoder(
ResourceTranscoder<GifBitmapWrapper, GlideDrawable> transcoder) {
super.transcoder(transcoder);
return this;
}
public final DrawableRequestBuilder<ModelType> crossFade() {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
return this;
}
public DrawableRequestBuilder<ModelType> crossFade(int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
return this;
}
public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
duration));
return this;
}
@Override
public DrawableRequestBuilder<ModelType> dontAnimate() {
super.dontAnimate();
return this;
}
@Override
public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) {
super.placeholder(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> fallback(Drawable drawable) {
super.fallback(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> fallback(int resourceId) {
super.fallback(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> error(Drawable drawable) {
super.error(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> listener(
RequestListener<? super ModelType, GlideDrawable> requestListener) {
super.listener(requestListener);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> override(int width, int height) {
super.override(width, height);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> sourceEncoder(Encoder<ImageVideoWrapper> sourceEncoder) {
super.sourceEncoder(sourceEncoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> dontTransform() {
super.dontTransform();
return this;
}
@Override
public DrawableRequestBuilder<ModelType> signature(Key signature) {
super.signature(signature);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> clone() {
return (DrawableRequestBuilder<ModelType>) super.clone();
}
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
@Override
void applyFitCenter() {
fitCenter();
}
@Override
void applyCenterCrop() {
centerCrop();
}
}
DrawableRequestBuilder中有很多個方法平道,這些方法其實(shí)就是Glide絕大多數(shù)的API了睹欲。里面有不少我們在上篇文章中已經(jīng)用過了,比如說placeholder()方法、error()方法窘疮、diskCacheStrategy()方法袋哼、override()方法等。當(dāng)然還有很多暫時還沒用到的API闸衫,我們會在后面的文章當(dāng)中學(xué)習(xí)涛贯。
到這里,第二步load()方法也就分析結(jié)束了蔚出。為什么呢疫蔓?因?yàn)槟銜l(fā)現(xiàn)DrawableRequestBuilder類中有一個into()方法(上述代碼第220行),也就是說身冬,最終load()方法返回的其實(shí)就是一個DrawableTypeRequest對象。那么接下來我們就要進(jìn)行第三步了岔乔,分析into()方法中的邏輯酥筝。
- into()
如果說前面兩步都是在準(zhǔn)備開胃小菜的話,那么現(xiàn)在終于要進(jìn)入主菜了雏门,因?yàn)閕nto()方法也是整個Glide圖片加載流程中邏輯最復(fù)雜的地方嘿歌。
不過從剛才的代碼來看,into()方法中并沒有任何邏輯茁影,只有一句super.into(view)宙帝。那么很顯然,into()方法的具體邏輯都是在DrawableRequestBuilder的父類當(dāng)中了募闲。
DrawableRequestBuilder的父類是GenericRequestBuilder步脓,我們來看一下GenericRequestBuilder類中的into()方法,如下所示:
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
這里前面一大堆的判斷邏輯我們都可以先不用管浩螺,等到后面文章講transform的時候會再進(jìn)行解釋靴患,現(xiàn)在我們只需要關(guān)注最后一行代碼。最后一行代碼先是調(diào)用了glide.buildImageViewTarget()方法要出,這個方法會構(gòu)建出一個Target對象鸳君,Target對象則是用來最終展示圖片用的,如果我們跟進(jìn)去的話會看到如下代碼:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
這里其實(shí)又是調(diào)用了ImageViewTargetFactory的buildTarget()方法患蹂,我們繼續(xù)跟進(jìn)去或颊,代碼如下所示:
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
可以看到,在buildTarget()方法中會根據(jù)傳入的class參數(shù)來構(gòu)建不同的Target對象传于。那如果你要分析這個class參數(shù)是從哪兒傳過來的囱挑,這可有得你分析了,簡單起見我直接幫大家梳理清楚格了。這個class參數(shù)其實(shí)基本上只有兩種情況看铆,如果你在使用Glide加載圖片的時候調(diào)用了asBitmap()方法,那么這里就會構(gòu)建出BitmapImageViewTarget對象盛末,否則的話構(gòu)建的都是GlideDrawableImageViewTarget對象弹惦。至于上述代碼中的DrawableImageViewTarget對象否淤,這個通常都是用不到的,我們可以暫時不用管它棠隐。
也就是說石抡,通過glide.buildImageViewTarget()方法,我們構(gòu)建出了一個GlideDrawableImageViewTarget對象助泽。那現(xiàn)在回到剛才into()方法的最后一行啰扛,可以看到,這里又將這個參數(shù)傳入到了GenericRequestBuilder另一個接收Target對象的into()方法當(dāng)中了嗡贺。我們來看一下這個into()方法的源碼:
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
這里我們還是只抓核心代碼隐解,其實(shí)只有兩行是最關(guān)鍵的,第15行調(diào)用buildRequest()方法構(gòu)建出了一個Request對象诫睬,還有第18行來執(zhí)行這個Request煞茫。
Request是用來發(fā)出加載圖片請求的,它是Glide中非常關(guān)鍵的一個組件摄凡。我們先來看buildRequest()方法是如何構(gòu)建Request對象的:
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
可以看到续徽,buildRequest()方法的內(nèi)部其實(shí)又調(diào)用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代碼雖然有點(diǎn)長亲澡,但是其中90%的代碼都是在處理縮略圖的。如果我們只追主線流程的話客情,那么只需要看第47行代碼就可以了。這里調(diào)用了obtainRequest()方法來獲取一個Request對象概页,而obtainRequest()方法中又去調(diào)用了GenericRequest的obtain()方法项鬼。注意這個obtain()方法需要傳入非常多的參數(shù),而其中很多的參數(shù)我們都是比較熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此瞄沙,我們就有理由猜測,剛才在load()方法中調(diào)用的所有API粟按,其實(shí)都是在這里組裝到Request對象當(dāng)中的幸冻。那么我們進(jìn)入到這個GenericRequest的obtain()方法瞧一瞧:
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
ResourceCallback {
...
public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(
LoadProvider<A, T, Z, R> loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target<R> target,
float sizeMultiplier,
Drawable placeholderDrawable,
int placeholderResourceId,
Drawable errorDrawable,
int errorResourceId,
Drawable fallbackDrawable,
int fallbackResourceId,
RequestListener<? super A, R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
Transformation<Z> transformation,
Class<R> transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory<R> animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
if (request == null) {
request = new GenericRequest<A, T, Z, R>();
}
request.init(loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderResourceId,
errorDrawable,
errorResourceId,
fallbackDrawable,
fallbackResourceId,
requestListener,
requestCoordinator,
engine,
transformation,
transcodeClass,
isMemoryCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
return request;
}
...
}
可以看到,這里在第33行去new了一個GenericRequest對象,并在最后一行返回安岂,也就是說桅锄,obtain()方法實(shí)際上獲得的就是一個GenericRequest對象。另外這里又在第35行調(diào)用了GenericRequest的init(),里面主要就是一些賦值的代碼,將傳入的這些參數(shù)賦值到GenericRequest的成員變量當(dāng)中,我們就不再跟進(jìn)去看了。
好,那現(xiàn)在解決了構(gòu)建Request對象的問題,接下來我們看一下這個Request對象又是怎么執(zhí)行的。回到剛才的into()方法嵌戈,你會發(fā)現(xiàn)在第18行調(diào)用了requestTracker.runRequest()方法來去執(zhí)行這個Request庵朝,那么我們跟進(jìn)去瞧一瞧,如下所示:
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
這里有一個簡單的邏輯判斷儡羔,就是先判斷Glide當(dāng)前是不是處理暫停狀態(tài)族操,如果不是暫停狀態(tài)就調(diào)用Request的begin()方法來執(zhí)行Request,否則的話就先將Request添加到待執(zhí)行隊(duì)列里面,等暫停狀態(tài)解除了之后再執(zhí)行枪狂。
暫停請求的功能仍然不是這篇文章所關(guān)心的,這里就直接忽略了宋渔,我們重點(diǎn)來看這個begin()方法州疾。由于當(dāng)前的Request對象是一個GenericRequest,因此這里就需要看GenericRequest中的begin()方法了薄嫡,如下所示:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
這里我們來注意幾個細(xì)節(jié)崩哩,首先如果model等于null妙痹,model也就是我們在第二步load()方法中傳入的圖片URL地址塘偎,這個時候會調(diào)用onException()方法。如果你跟到onException()方法里面去看看女责,你會發(fā)現(xiàn)它最終會調(diào)用到一個setErrorPlaceholder()當(dāng)中纺蛆,如下所示:
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
這個方法中會先去獲取一個error的占位圖禁炒,如果獲取不到的話會再去獲取一個loading占位圖汰瘫,然后調(diào)用target.onLoadFailed()方法并將占位圖傳入。那么onLoadFailed()方法中做了什么呢?我們看一下:
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
...
@Override
public void onLoadStarted(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setImageDrawable(errorDrawable);
}
...
}
很簡單添坊,其實(shí)就是將這張error占位圖顯示到ImageView上而已蝉稳,因?yàn)楝F(xiàn)在出現(xiàn)了異常,沒辦法展示正常的圖片了掘鄙。而如果你仔細(xì)看下剛才begin()方法的第15行耘戚,你會發(fā)現(xiàn)它又調(diào)用了一個target.onLoadStarted()方法,并傳入了一個loading占位圖操漠,在也就說收津,在圖片請求開始之前,會先使用這張占位圖代替最終的圖片顯示浊伙。這也是我們在上一篇文章中學(xué)過的placeholder()和error()這兩個占位圖API底層的實(shí)現(xiàn)原理撞秋。
好,那么我們繼續(xù)回到begin()方法嚣鄙。剛才講了占位圖的實(shí)現(xiàn)吻贿,那么具體的圖片加載又是從哪里開始的呢?是在begin()方法的第10行和第12行哑子。這里要分兩種情況舅列,一種是你使用了override() API為圖片指定了一個固定的寬高,一種是沒有指定卧蜓。如果指定了的話帐要,就會執(zhí)行第10行代碼,調(diào)用onSizeReady()方法烦却。如果沒指定的話宠叼,就會執(zhí)行第12行代碼先巴,調(diào)用target.getSize()方法其爵。這個target.getSize()方法的內(nèi)部會根據(jù)ImageView的layout_width和layout_height值做一系列的計算冒冬,來算出圖片應(yīng)該的寬高。具體的計算細(xì)節(jié)我就不帶著大家分析了摩渺,總之在計算完之后简烤,它也會調(diào)用onSizeReady()方法。也就是說摇幻,不管是哪種情況横侦,最終都會調(diào)用到onSizeReady()方法,在這里進(jìn)行下一步操作绰姻。那么我們跟到這個方法里面來:
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
從這里開始枉侧,真正復(fù)雜的地方來了,我們需要慢慢進(jìn)行分析狂芋。先來看一下榨馁,在第12行調(diào)用了loadProvider.getModelLoader()方法,那么我們第一個要搞清楚的就是帜矾,這個loadProvider是什么翼虫?要搞清楚這點(diǎn),需要先回到第二步的load()方法當(dāng)中屡萤。還記得load()方法是返回一個DrawableTypeRequest對象嗎珍剑?剛才我們只是分析了DrawableTypeRequest當(dāng)中的asBitmap()和asGif()方法,并沒有仔細(xì)看它的構(gòu)造函數(shù)死陆,現(xiàn)在我們重新來看一下DrawableTypeRequest類的構(gòu)造函數(shù):
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
...
}
可以看到招拙,這里在第29行,也就是構(gòu)造函數(shù)中措译,調(diào)用了一個buildProvider()方法迫像,并把streamModelLoader和fileDescriptorModelLoader等參數(shù)傳入到這個方法中,這兩個ModelLoader就是之前在loadGeneric()方法中構(gòu)建出來的瞳遍。
那么我們再來看一下buildProvider()方法里面做了什么闻妓,在第16行調(diào)用了glide.buildTranscoder()方法來構(gòu)建一個ResourceTranscoder,它是用于對圖片進(jìn)行轉(zhuǎn)碼的掠械,由于ResourceTranscoder是一個接口由缆,這里實(shí)際會構(gòu)建出一個GifBitmapWrapperDrawableTranscoder對象。
接下來在第18行調(diào)用了glide.buildDataProvider()方法來構(gòu)建一個DataLoadProvider猾蒂,它是用于對圖片進(jìn)行編解碼的均唉,由于DataLoadProvider是一個接口,這里實(shí)際會構(gòu)建出一個ImageVideoGifDrawableLoadProvider對象肚菠。
然后在第20行舔箭,new了一個ImageVideoModelLoader的實(shí)例,并把之前l(fā)oadGeneric()方法中構(gòu)建的兩個ModelLoader封裝到了ImageVideoModelLoader當(dāng)中。
最后层扶,在第22行箫章,new出一個FixedLoadProvider,并把剛才構(gòu)建的出來的GifBitmapWrapperDrawableTranscoder镜会、ImageVideoModelLoader檬寂、ImageVideoGifDrawableLoadProvider都封裝進(jìn)去,這個也就是onSizeReady()方法中的loadProvider了戳表。
好的桶至,那么我們回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行匾旭,分別調(diào)用了loadProvider的getModelLoader()方法和getTranscoder()方法镣屹,那么得到的對象也就是剛才我們分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。而在第13行价涝,又調(diào)用了ImageVideoModelLoader的getResourceFetcher()方法野瘦,這里我們又需要跟進(jìn)去瞧一瞧了,代碼如下所示:
public class ImageVideoModelLoader<A> implements ModelLoader<A, ImageVideoWrapper> {
private static final String TAG = "IVML";
private final ModelLoader<A, InputStream> streamLoader;
private final ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader;
public ImageVideoModelLoader(ModelLoader<A, InputStream> streamLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader) {
if (streamLoader == null && fileDescriptorLoader == null) {
throw new NullPointerException("At least one of streamLoader and fileDescriptorLoader must be non null");
}
this.streamLoader = streamLoader;
this.fileDescriptorLoader = fileDescriptorLoader;
}
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
DataFetcher<InputStream> streamFetcher = null;
if (streamLoader != null) {
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
private final DataFetcher<InputStream> streamFetcher;
private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;
public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
this.streamFetcher = streamFetcher;
this.fileDescriptorFetcher = fileDescriptorFetcher;
}
...
}
}
可以看到飒泻,在第20行會先調(diào)用streamLoader.getResourceFetcher()方法獲取一個DataFetcher鞭光,而這個streamLoader其實(shí)就是我們在loadGeneric()方法中構(gòu)建出的StreamStringLoader,調(diào)用它的getResourceFetcher()方法會得到一個HttpUrlFetcher對象泞遗。然后在第28行new出了一個ImageVideoFetcher對象惰许,并把獲得的HttpUrlFetcher對象傳進(jìn)去。也就是說史辙,ImageVideoModelLoader的getResourceFetcher()方法得到的是一個ImageVideoFetcher汹买。
那么我們再次回到onSizeReady()方法,在onSizeReady()方法的第23行聊倔,這里將剛才獲得的ImageVideoFetcher晦毙、GifBitmapWrapperDrawableTranscoder等等一系列的值一起傳入到了Engine的load()方法當(dāng)中。接下來我們就要看一看耙蔑,這個Engine的load()方法當(dāng)中见妒,到底做了什么?代碼如下所示:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
load()方法中的代碼雖然有點(diǎn)長甸陌,但大多數(shù)的代碼都是在處理緩存的须揣。關(guān)于緩存的內(nèi)容我們會在下一篇文章當(dāng)中學(xué)習(xí),現(xiàn)在只需要從第45行看起就行钱豁。這里構(gòu)建了一個EngineJob耻卡,它的主要作用就是用來開啟線程的,為后面的異步加載圖片做準(zhǔn)備牲尺。接下來第46行創(chuàng)建了一個DecodeJob對象卵酪,從名字上來看,它好像是用來對圖片進(jìn)行解碼的,但實(shí)際上它的任務(wù)十分繁重溃卡,待會我們就知道了溢豆。繼續(xù)往下看,第48行創(chuàng)建了一個EngineRunnable對象塑煎,并且在51行調(diào)用了EngineJob的start()方法來運(yùn)行EngineRunnable對象,這實(shí)際上就是讓EngineRunnable的run()方法在子線程當(dāng)中執(zhí)行了臭蚁。那么我們現(xiàn)在就可以去看看EngineRunnable的run()方法里做了些什么最铁,如下所示:
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
這個方法中的代碼并不多,但我們?nèi)匀贿€是要抓重點(diǎn)垮兑。在第9行冷尉,這里調(diào)用了一個decode()方法,并且這個方法返回了一個Resource對象系枪∪干冢看上去所有的邏輯應(yīng)該都在這個decode()方法執(zhí)行的了,那我們跟進(jìn)去瞧一瞧:
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
decode()方法中又分了兩種情況私爷,從緩存當(dāng)中去decode圖片的話就會執(zhí)行decodeFromCache()雾棺,否則的話就執(zhí)行decodeFromSource()。本篇文章中我們不討論緩存的情況衬浑,那么就直接來看decodeFromSource()方法的代碼吧捌浩,如下所示:
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
這里又調(diào)用了DecodeJob的decodeFromSource()方法。剛才已經(jīng)說了工秩,DecodeJob的任務(wù)十分繁重尸饺,我們繼續(xù)跟進(jìn)看一看吧:
class DecodeJob<A, T, Z> {
...
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
...
}
主要的方法就這些,我都幫大家提取出來了助币。那么我們先來看一下decodeFromSource()方法浪听,其實(shí)它的工作分為兩部,第一步是調(diào)用decodeSource()方法來獲得一個Resource對象眉菱,第二步是調(diào)用transformEncodeAndTranscode()方法來處理這個Resource對象迹栓。
那么我們先來看第一步,decodeSource()方法中的邏輯也并不復(fù)雜俭缓,首先在第14行調(diào)用了fetcher.loadData()方法迈螟。那么這個fetcher是什么呢?其實(shí)就是剛才在onSizeReady()方法中得到的ImageVideoFetcher對象尔崔,這里調(diào)用它的loadData()方法答毫,代碼如下所示:
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
InputStream is = null;
if (streamFetcher != null) {
try {
is = streamFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
return new ImageVideoWrapper(is, fileDescriptor);
}
可以看到,在ImageVideoFetcher的loadData()方法的第6行季春,這里又去調(diào)用了streamFetcher.loadData()方法洗搂,那么這個streamFetcher是什么呢?自然就是剛才在組裝ImageVideoFetcher對象時傳進(jìn)來的HttpUrlFetcher了。因此這里又會去調(diào)用HttpUrlFetcher的loadData()方法耘拇,那么我們繼續(xù)跟進(jìn)去瞧一瞧:
public class HttpUrlFetcher implements DataFetcher<InputStream> {
...
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new IOException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
...
}
經(jīng)過一層一層地跋山涉水撵颊,我們終于在這里找到網(wǎng)絡(luò)通訊的代碼了!之前有朋友跟我講過惫叛,說Glide的源碼實(shí)在是太復(fù)雜了倡勇,甚至連網(wǎng)絡(luò)請求是在哪里發(fā)出去的都找不到。我們也是經(jīng)過一段一段又一段的代碼跟蹤嘉涌,終于把網(wǎng)絡(luò)請求的代碼給找出來了妻熊,實(shí)在是太不容易了。
不過也別高興得太早仑最,現(xiàn)在離最終分析完還早著呢扔役。可以看到警医,loadData()方法只是返回了一個InputStream亿胸,服務(wù)器返回的數(shù)據(jù)連讀都還沒開始讀呢。所以我們還是要靜下心來繼續(xù)分析预皇,回到剛才ImageVideoFetcher的loadData()方法中侈玄,在這個方法的最后一行,創(chuàng)建了一個ImageVideoWrapper對象吟温,并把剛才得到的InputStream作為參數(shù)傳了進(jìn)去拗馒。
然后我們回到再上一層,也就是DecodeJob的decodeSource()方法當(dāng)中溯街,在得到了這個ImageVideoWrapper對象之后诱桂,緊接著又將這個對象傳入到了decodeFromSourceData()當(dāng)中,來去解碼這個對象呈昔。decodeFromSourceData()方法的代碼如下所示:
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
可以看到挥等,這里在第7行調(diào)用了loadProvider.getSourceDecoder().decode()方法來進(jìn)行解碼。loadProvider就是剛才在onSizeReady()方法中得到的FixedLoadProvider堤尾,而getSourceDecoder()得到的則是一個GifBitmapWrapperResourceDecoder對象肝劲,也就是要調(diào)用這個對象的decode()方法來對圖片進(jìn)行解碼。那么我們來看下GifBitmapWrapperResourceDecoder的代碼:
public class GifBitmapWrapperResourceDecoder implements ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> {
...
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
result = decodeStream(source, width, height, bytes);
} else {
result = decodeBitmapWrapper(source, width, height);
}
return result;
}
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
throws IOException {
InputStream bis = streamFactory.build(source.getStream(), bytes);
bis.mark(MARK_LIMIT_BYTES);
ImageHeaderParser.ImageType type = parser.parse(bis);
bis.reset();
GifBitmapWrapper result = null;
if (type == ImageHeaderParser.ImageType.GIF) {
result = decodeGifWrapper(bis, width, height);
}
// Decoding the gif may fail even if the type matches.
if (result == null) {
// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
// pass in a new source containing the buffered stream rather than the original stream.
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
}
return result;
}
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
...
}
首先郭宝,在decode()方法中辞槐,又去調(diào)用了另外一個decode()方法的重載。然后在第23行調(diào)用了decodeStream()方法粘室,準(zhǔn)備從服務(wù)器返回的流當(dāng)中讀取數(shù)據(jù)榄檬。decodeStream()方法中會先從流中讀取2個字節(jié)的數(shù)據(jù),來判斷這張圖是GIF圖還是普通的靜圖衔统,如果是GIF圖就調(diào)用decodeGifWrapper()方法來進(jìn)行解碼鹿榜,如果是普通的靜圖就用調(diào)用decodeBitmapWrapper()方法來進(jìn)行解碼海雪。這里我們只分析普通靜圖的實(shí)現(xiàn)流程,GIF圖的實(shí)現(xiàn)有點(diǎn)過于復(fù)雜了舱殿,無法在本篇文章當(dāng)中分析奥裸。
然后我們來看一下decodeBitmapWrapper()方法,這里在第52行調(diào)用了bitmapDecoder.decode()方法沪袭。這個bitmapDecoder是一個ImageVideoBitmapDecoder對象湾宙,那么我們來看一下它的代碼,如下所示:
public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> {
private final ResourceDecoder<InputStream, Bitmap> streamDecoder;
private final ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder;
public ImageVideoBitmapDecoder(ResourceDecoder<InputStream, Bitmap> streamDecoder,
ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder) {
this.streamDecoder = streamDecoder;
this.fileDescriptorDecoder = fileDescriptorDecoder;
}
@Override
public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException {
Resource<Bitmap> result = null;
InputStream is = source.getStream();
if (is != null) {
try {
result = streamDecoder.decode(is, width, height);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
}
}
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
...
}
代碼并不復(fù)雜冈绊,在第14行先調(diào)用了source.getStream()來獲取到服務(wù)器返回的InputStream侠鳄,然后在第17行調(diào)用streamDecoder.decode()方法進(jìn)行解碼。streamDecode是一個StreamBitmapDecoder對象焚碌,那么我們再來看這個類的源碼畦攘,如下所示:
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
...
private final Downsampler downsampler;
private BitmapPool bitmapPool;
private DecodeFormat decodeFormat;
public StreamBitmapDecoder(Downsampler downsampler, BitmapPool bitmapPool, DecodeFormat decodeFormat) {
this.downsampler = downsampler;
this.bitmapPool = bitmapPool;
this.decodeFormat = decodeFormat;
}
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
...
}
可以看到霸妹,它的decode()方法又去調(diào)用了Downsampler的decode()方法十电。接下來又到了激動人心的時刻了,Downsampler的代碼如下所示:
public abstract class Downsampler implements BitmapDecoder<InputStream> {
...
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
// if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still attempt to decode the
// full image even when the header decode fails and/or overflows our read buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config config = getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
/**
* A method for getting the dimensions of an image from the given InputStream.
*
* @param is The InputStream representing the image.
* @param options The options to pass to
* {@link BitmapFactory#decodeStream(InputStream, android.graphics.Rect,
* BitmapFactory.Options)}.
* @return an array containing the dimensions of the image in the form {width, height}.
*/
public int[] getDimensions(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
options.inJustDecodeBounds = true;
decodeStream(is, bufferedStream, options);
options.inJustDecodeBounds = false;
return new int[] { options.outWidth, options.outHeight };
}
private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
// This is large, but jpeg headers are not size bounded so we need something large enough to minimize
// the possibility of not being able to fit enough of the header in the buffer to get the image size so
// that we don't fail to load images. The BufferedInputStream will create a new buffer of 2x the
// original size each time we use up the buffer space without passing the mark so this is a maximum
// bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
is.mark(MARK_POSITION);
} else {
// Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
// unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
// current buffer size here. See issue #225.
bufferedStream.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
try {
if (options.inJustDecodeBounds) {
is.reset();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
+ " sample=" + options.inSampleSize, e);
}
}
return result;
}
...
}
可以看到叹螟,對服務(wù)器返回的InputStream的讀取鹃骂,以及對圖片的加載全都在這里了。當(dāng)然這里其實(shí)處理了很多的邏輯罢绽,包括對圖片的壓縮畏线,甚至還有旋轉(zhuǎn)、圓角等邏輯處理良价,但是我們目前只需要關(guān)注主線邏輯就行了寝殴。decode()方法執(zhí)行之后,會返回一個Bitmap對象明垢,那么圖片在這里其實(shí)也就已經(jīng)被加載出來了蚣常,剩下的工作就是如果讓這個Bitmap顯示到界面上,我們繼續(xù)往下分析痊银。
回到剛才的StreamBitmapDecoder當(dāng)中抵蚊,你會發(fā)現(xiàn),它的decode()方法返回的是一個Resource<Bitmap>對象溯革。而我們從Downsampler中得到的是一個Bitmap對象贞绳,因此這里在第18行又調(diào)用了BitmapResource.obtain()方法,將Bitmap對象包裝成了Resource<Bitmap>對象致稀。代碼如下所示:
public class BitmapResource implements Resource<Bitmap> {
private final Bitmap bitmap;
private final BitmapPool bitmapPool;
/**
* Returns a new {@link BitmapResource} wrapping the given {@link Bitmap} if the Bitmap is non-null or null if the
* given Bitmap is null.
*
* @param bitmap A Bitmap.
* @param bitmapPool A non-null {@link BitmapPool}.
*/
public static BitmapResource obtain(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
return null;
} else {
return new BitmapResource(bitmap, bitmapPool);
}
}
public BitmapResource(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
throw new NullPointerException("Bitmap must not be null");
}
if (bitmapPool == null) {
throw new NullPointerException("BitmapPool must not be null");
}
this.bitmap = bitmap;
this.bitmapPool = bitmapPool;
}
@Override
public Bitmap get() {
return bitmap;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
if (!bitmapPool.put(bitmap)) {
bitmap.recycle();
}
}
}
BitmapResource的源碼也非常簡單冈闭,經(jīng)過這樣一層包裝之后,如果我還需要獲取Bitmap抖单,只需要調(diào)用Resource<Bitmap>的get()方法就可以了拒秘。
然后我們需要一層層繼續(xù)向上返回号显,StreamBitmapDecoder會將值返回到ImageVideoBitmapDecoder當(dāng)中,而ImageVideoBitmapDecoder又會將值返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法當(dāng)中躺酒。由于代碼隔得有點(diǎn)太遠(yuǎn)了押蚤,我重新把decodeBitmapWrapper()方法的代碼貼一下:
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
可以看到,decodeBitmapWrapper()方法返回的是一個GifBitmapWrapper對象羹应。因此揽碘,這里在第5行,又將Resource<Bitmap>封裝到了一個GifBitmapWrapper對象當(dāng)中园匹。這個GifBitmapWrapper顧名思義雳刺,就是既能封裝GIF,又能封裝Bitmap裸违,從而保證了不管是什么類型的圖片Glide都能從容應(yīng)對掖桦。我們順便來看下GifBitmapWrapper的源碼吧,如下所示:
public class GifBitmapWrapper {
private final Resource<GifDrawable> gifResource;
private final Resource<Bitmap> bitmapResource;
public GifBitmapWrapper(Resource<Bitmap> bitmapResource, Resource<GifDrawable> gifResource) {
if (bitmapResource != null && gifResource != null) {
throw new IllegalArgumentException("Can only contain either a bitmap resource or a gif resource, not both");
}
if (bitmapResource == null && gifResource == null) {
throw new IllegalArgumentException("Must contain either a bitmap resource or a gif resource");
}
this.bitmapResource = bitmapResource;
this.gifResource = gifResource;
}
/**
* Returns the size of the wrapped resource.
*/
public int getSize() {
if (bitmapResource != null) {
return bitmapResource.getSize();
} else {
return gifResource.getSize();
}
}
/**
* Returns the wrapped {@link Bitmap} resource if it exists, or null.
*/
public Resource<Bitmap> getBitmapResource() {
return bitmapResource;
}
/**
* Returns the wrapped {@link GifDrawable} resource if it exists, or null.
*/
public Resource<GifDrawable> getGifResource() {
return gifResource;
}
}
還是比較簡單的供汛,就是分別對gifResource和bitmapResource做了一層封裝而已枪汪,相信沒有什么解釋的必要。
然后這個GifBitmapWrapper對象會一直向上返回怔昨,返回到GifBitmapWrapperResourceDecoder最外層的decode()方法的時候雀久,會對它再做一次封裝,如下所示:
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
可以看到趁舀,這里在第11行赖捌,又將GifBitmapWrapper封裝到了一個GifBitmapWrapperResource對象當(dāng)中,最終返回的是一個Resource<GifBitmapWrapper>對象矮烹。這個GifBitmapWrapperResource和剛才的BitmapResource是相似的越庇,它們都實(shí)現(xiàn)的Resource接口,都可以通過get()方法來獲取封裝起來的具體內(nèi)容奉狈。GifBitmapWrapperResource的源碼如下所示:
public class GifBitmapWrapperResource implements Resource<GifBitmapWrapper> {
private final GifBitmapWrapper data;
public GifBitmapWrapperResource(GifBitmapWrapper data) {
if (data == null) {
throw new NullPointerException("Data must not be null");
}
this.data = data;
}
@Override
public GifBitmapWrapper get() {
return data;
}
@Override
public int getSize() {
return data.getSize();
}
@Override
public void recycle() {
Resource<Bitmap> bitmapResource = data.getBitmapResource();
if (bitmapResource != null) {
bitmapResource.recycle();
}
Resource<GifDrawable> gifDataResource = data.getGifResource();
if (gifDataResource != null) {
gifDataResource.recycle();
}
}
}
經(jīng)過這一層的封裝之后卤唉,我們從網(wǎng)絡(luò)上得到的圖片就能夠以Resource接口的形式返回,并且還能同時處理Bitmap圖片和GIF圖片這兩種情況嘹吨。
那么現(xiàn)在我們可以回到DecodeJob當(dāng)中了搬味,它的decodeFromSourceData()方法返回的是一個Resource<T>對象,其實(shí)也就是Resource<GifBitmapWrapper>對象了蟀拷。然后繼續(xù)向上返回碰纬,最終返回到decodeFromSource()方法當(dāng)中,如下所示:
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
剛才我們就是從這里跟進(jìn)到decodeSource()方法當(dāng)中问芬,然后執(zhí)行了一大堆一大堆的邏輯悦析,最終得到了這個Resource<T>對象。然而你會發(fā)現(xiàn)此衅,decodeFromSource()方法最終返回的卻是一個Resource<Z>對象强戴,那么這到底是怎么回事呢亭螟?我們就需要跟進(jìn)到transformEncodeAndTranscode()方法來瞧一瞧了,代碼如下所示:
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
private Resource<Z> transcode(Resource<T> transformed) {
if (transformed == null) {
return null;
}
return transcoder.transcode(transformed);
}
首先骑歹,這個方法開頭的幾行transform還有cache预烙,這都是我們后面才會學(xué)習(xí)的東西,現(xiàn)在不用管它們就可以了道媚。需要注意的是第9行扁掸,這里調(diào)用了一個transcode()方法,就把Resource<T>對象轉(zhuǎn)換成Resource<Z>對象了最域。
而transcode()方法中又是調(diào)用了transcoder的transcode()方法谴分,那么這個transcoder是什么呢?其實(shí)這也是Glide源碼特別難懂的原因之一镀脂,就是它用到的很多對象都是很早很早之前就初始化的牺蹄,在初始化的時候你可能完全就沒有留意過它,因?yàn)橐粫r半會根本就用不著薄翅,但是真正需要用到的時候你卻早就記不起來這個對象是從哪兒來的了沙兰。
那么這里我來提醒一下大家吧,在第二步load()方法返回的那個DrawableTypeRequest對象匿刮,它的構(gòu)建函數(shù)中去構(gòu)建了一個FixedLoadProvider對象毡代,然后我們將三個參數(shù)傳入到了FixedLoadProvider當(dāng)中禁添,其中就有一個GifBitmapWrapperDrawableTranscoder對象。后來在onSizeReady()方法中獲取到了這個參數(shù)装获,并傳遞到了Engine當(dāng)中伪节,然后又由Engine傳遞到了DecodeJob當(dāng)中光羞。因此,這里的transcoder其實(shí)就是這個GifBitmapWrapperDrawableTranscoder對象怀大。那么我們來看一下它的源碼:
public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> {
private final ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder;
public GifBitmapWrapperDrawableTranscoder(
ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder) {
this.bitmapDrawableResourceTranscoder = bitmapDrawableResourceTranscoder;
}
@Override
public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) {
GifBitmapWrapper gifBitmap = toTranscode.get();
Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource();
final Resource<? extends GlideDrawable> result;
if (bitmapResource != null) {
result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
} else {
result = gifBitmap.getGifResource();
}
return (Resource<GlideDrawable>) result;
}
...
}
這里我來簡單解釋一下纱兑,GifBitmapWrapperDrawableTranscoder的核心作用就是用來轉(zhuǎn)碼的。因?yàn)镚ifBitmapWrapper是無法直接顯示到ImageView上面的化借,只有Bitmap或者Drawable才能顯示到ImageView上潜慎。因此,這里的transcode()方法先從Resource<GifBitmapWrapper>中取出GifBitmapWrapper對象蓖康,然后再從GifBitmapWrapper中取出Resource<Bitmap>對象铐炫。
接下來做了一個判斷,如果Resource<Bitmap>為空蒜焊,那么說明此時加載的是GIF圖倒信,直接調(diào)用getGifResource()方法將圖片取出即可,因?yàn)镚lide用于加載GIF圖片是使用的GifDrawable這個類泳梆,它本身就是一個Drawable對象了鳖悠。而如果Resource<Bitmap>不為空榜掌,那么就需要再做一次轉(zhuǎn)碼,將Bitmap轉(zhuǎn)換成Drawable對象才行乘综,因?yàn)橐WC靜圖和動圖的類型一致性憎账,不然邏輯上是不好處理的。
這里在第15行又進(jìn)行了一次轉(zhuǎn)碼卡辰,是調(diào)用的GlideBitmapDrawableTranscoder對象的transcode()方法鼠哥,代碼如下所示:
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
private final Resources resources;
private final BitmapPool bitmapPool;
public GlideBitmapDrawableTranscoder(Context context) {
this(context.getResources(), Glide.get(context).getBitmapPool());
}
public GlideBitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) {
this.resources = resources;
this.bitmapPool = bitmapPool;
}
@Override
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
...
}
可以看到,這里new出了一個GlideBitmapDrawable對象看政,并把Bitmap封裝到里面朴恳。然后對GlideBitmapDrawable再進(jìn)行一次封裝,返回一個Resource<GlideBitmapDrawable>對象允蚣。
現(xiàn)在再返回到GifBitmapWrapperDrawableTranscoder的transcode()方法中于颖,你會發(fā)現(xiàn)它們的類型就一致了。因?yàn)椴还苁庆o圖的Resource<GlideBitmapDrawable>對象嚷兔,還是動圖的Resource<GifDrawable>對象森渐,它們都是屬于父類Resource<GlideDrawable>對象的。因此transcode()方法也是直接返回了Resource<GlideDrawable>冒晰,而這個Resource<GlideDrawable>其實(shí)也就是轉(zhuǎn)換過后的Resource<Z>了同衣。
那么我們繼續(xù)回到DecodeJob當(dāng)中,它的decodeFromSource()方法得到了Resource<Z>對象壶运,當(dāng)然也就是Resource<GlideDrawable>對象耐齐。然后繼續(xù)向上返回會回到EngineRunnable的decodeFromSource()方法,再回到decode()方法蒋情,再回到run()方法當(dāng)中埠况。那么我們重新再貼一下EngineRunnable run()方法的源碼:
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
也就是說,經(jīng)過第9行decode()方法的執(zhí)行棵癣,我們最終得到了這個Resource<GlideDrawable>對象辕翰,那么接下來就是如何將它顯示出來了”芬辏可以看到喜命,這里在第25行調(diào)用了onLoadComplete()方法,表示圖片加載已經(jīng)完成了河劝,代碼如下所示:
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
這個manager就是EngineJob對象壁榕,因此這里實(shí)際上調(diào)用的是EngineJob的onResourceReady()方法,代碼如下所示:
class EngineJob implements EngineRunnable.EngineRunnableManager {
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
private final List<ResourceCallback> cbs = new ArrayList<ResourceCallback>();
...
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
cb.onResourceReady(engineResource);
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
@Override
public void onException(final Exception e) {
this.exception = e;
MAIN_THREAD_HANDLER.obtainMessage(MSG_EXCEPTION, this).sendToTarget();
}
private void handleExceptionOnMainThread() {
if (isCancelled) {
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received an exception without any callbacks to notify");
}
hasException = true;
listener.onEngineJobComplete(key, null);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
cb.onException(exception);
}
}
}
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
...
}
可以看到丧裁,這里在onResourceReady()方法使用Handler發(fā)出了一條MSG_COMPLETE消息护桦,那么在MainThreadCallback的handleMessage()方法中就會收到這條消息。從這里開始煎娇,所有的邏輯又回到主線程當(dāng)中進(jìn)行了二庵,因?yàn)楹芸炀托枰耈I了贪染。
然后在第72行調(diào)用了handleResultOnMainThread()方法,這個方法中又通過一個循環(huán)催享,調(diào)用了所有ResourceCallback的onResourceReady()方法杭隙。那么這個ResourceCallback是什么呢?答案在addCallback()方法當(dāng)中因妙,它會向cbs集合中去添加ResourceCallback痰憎。那么這個addCallback()方法又是哪里調(diào)用的呢?其實(shí)調(diào)用的地方我們早就已經(jīng)看過了攀涵,只不過之前沒有注意铣耘,現(xiàn)在重新來看一下Engine的load()方法,如下所示:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority,
boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
...
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
這次把目光放在第18行上面以故,看到了嗎蜗细?就是在這里調(diào)用的EngineJob的addCallback()方法來注冊的一個ResourceCallback。那么接下來的問題就是怒详,Engine.load()方法的ResourceCallback參數(shù)又是誰傳過來的呢炉媒?這就需要回到GenericRequest的onSizeReady()方法當(dāng)中了,我們看到ResourceCallback是load()方法的最后一個參數(shù)昆烁,那么在onSizeReady()方法中調(diào)用load()方法時傳入的最后一個參數(shù)是什么吊骤?代碼如下所示:
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
ResourceCallback {
...
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation,
transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
...
}
請將目光鎖定在第29行的最后一個參數(shù),this静尼。沒錯白粉,就是this。GenericRequest本身就實(shí)現(xiàn)了ResourceCallback的接口茅郎,因此EngineJob的回調(diào)最終其實(shí)就是回調(diào)到了GenericRequest的onResourceReady()方法當(dāng)中了蜗元,代碼如下所示:
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("Expected to receive an object of " + transcodeClass
+ " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
+ " inside Resource{" + resource + "}."
+ (received != null ? "" : " "
+ "To indicate failure return a null Resource object, "
+ "rather than a Resource object containing null data.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
這里有兩個onResourceReady()方法或渤,首先在第一個onResourceReady()方法當(dāng)中系冗,調(diào)用resource.get()方法獲取到了封裝的圖片對象,也就是GlideBitmapDrawable對象薪鹦,或者是GifDrawable對象掌敬。然后將這個值傳入到了第二個onResourceReady()方法當(dāng)中,并在第36行調(diào)用了target.onResourceReady()方法池磁。
那么這個target又是什么呢奔害?這個又需要向上翻很久了,在第三步into()方法的一開始地熄,我們就分析了在into()方法的最后一行华临,調(diào)用了glide.buildImageViewTarget()方法來構(gòu)建出一個Target,而這個Target就是一個GlideDrawableImageViewTarget對象端考。
那么我們?nèi)タ碐lideDrawableImageViewTarget的源碼就可以了雅潭,如下所示:
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
private static final float SQUARE_RATIO_MARGIN = 0.05f;
private int maxLoopCount;
private GlideDrawable resource;
public GlideDrawableImageViewTarget(ImageView view) {
this(view, GlideDrawable.LOOP_FOREVER);
}
public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
super(view);
this.maxLoopCount = maxLoopCount;
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
@Override
public void onStart() {
if (resource != null) {
resource.start();
}
}
@Override
public void onStop() {
if (resource != null) {
resource.stop();
}
}
}
在GlideDrawableImageViewTarget的onResourceReady()方法中做了一些邏輯處理揭厚,包括如果是GIF圖片的話,就調(diào)用resource.start()方法開始播放圖片扶供,但是好像并沒有看到哪里有將GlideDrawable顯示到ImageView上的邏輯筛圆。
確實(shí)沒有,不過父類里面有椿浓,這里在第25行調(diào)用了super.onResourceReady()方法太援,GlideDrawableImageViewTarget的父類是ImageViewTarget,我們來看下它的代碼吧:
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
...
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
可以看到扳碍,在ImageViewTarget的onResourceReady()方法當(dāng)中調(diào)用了setResource()方法提岔,而ImageViewTarget的setResource()方法是一個抽象方法,具體的實(shí)現(xiàn)還是在子類那邊實(shí)現(xiàn)的笋敞。
那子類的setResource()方法是怎么實(shí)現(xiàn)的呢唧垦?回頭再來看一下GlideDrawableImageViewTarget的setResource()方法,沒錯液样,調(diào)用的view.setImageDrawable()方法振亮,而這個view就是ImageView。代碼執(zhí)行到這里鞭莽,圖片終于也就顯示出來了坊秸。
那么,我們對Glide執(zhí)行流程的源碼分析澎怒,到這里也終于結(jié)束了褒搔。
總結(jié)
真是好長的一篇文章,這也可能是我目前所寫過的最長的一篇文章了喷面。如果你之前沒有讀過Glide的源碼星瘾,真的很難相信,這短短一行代碼:
Glide.with(this).load(url).into(imageView);
1
背后竟然蘊(yùn)藏著如此極其復(fù)雜的邏輯吧惧辈?
不過Glide也并不是有意要將代碼寫得如此復(fù)雜琳状,實(shí)在是因?yàn)镚lide的功能太強(qiáng)大了,而上述代碼只是使用了Glide最最基本的功能而已盒齿。
現(xiàn)在通過兩篇文章念逞,我們已經(jīng)掌握了Glide的基本用法,并且通過閱讀源碼了解了Glide總的執(zhí)行流程边翁。接下來的幾篇文章翎承,我會帶大家深入到Glide源碼的某一處細(xì)節(jié),學(xué)習(xí)Glide更多的高級使用技巧符匾,感興趣的朋友請繼續(xù)閱讀 Android圖片加載框架最全解析(三)叨咖,深入探究Glide的緩存機(jī)制 。