當前位置:
首頁 > 最新 > Android圖片載入框架最全解析二,從源碼的角度理解Glide的執行流程

Android圖片載入框架最全解析二,從源碼的角度理解Glide的執行流程

在本系列的上一篇文章中,我們學習了Glide的基本用法,體驗了這個圖片載入框架的強大功能,以及它非常簡便的API。還沒有看過上一篇文章的朋友,建議先去閱讀 Android圖片載入框架最全解析(一),Glide的基本用法 。

在多數情況下,我們想要在界面上載入並展示一張圖片只需要一行代碼就能實現,如下所示:

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

雖說只有這簡簡單單的一行代碼,但大家可能不知道的是,Glide在背後幫我們默默執行了成噸的工作。這個形容詞我想了很久,因為我覺得用非常多這個形容詞不足以描述Glide背後的工作量,我查到的英文資料是用tons of work來進行形容的,因此我覺得這裡使用成噸來形容更加貼切一些。

雖說我們在平時使用Glide的時候格外地簡單和方便,但是知其然也要知其所以然。那麼今天我們就來解析一下Glide的源碼,看看它在這些簡單用法的背後,到底執行了多麼複雜的工作。

如何閱讀源碼

在開始解析Glide源碼之前,我想先和大家談一下該如何閱讀源碼,這個問題也是我平時被問得比較多的,因為很多人都覺得閱讀源碼是一件比較困難的事情。

那麼閱讀源碼到底困難嗎?這個當然主要還是要視具體的源碼而定。比如同樣是圖片載入框架,我讀Volley的源碼時就感覺酣暢淋漓,並且對Volley的架構設計和代碼質量深感佩服。讀Glide的源碼時卻讓我相當痛苦,代碼極其難懂。當然這裡我並不是說Glide的代碼寫得不好,只是因為Glide和複雜程度和Volley完全不是在一個量級上的。

那麼,雖然源碼的複雜程度是外在的不可變條件,但我們卻可以通過一些技巧來提升自己閱讀源碼的能力。這裡我和大家分享一下我平時閱讀源碼時所使用的技巧,簡單概括就是八個字:抽絲剝繭、點到即止。應該認準一個功能點,然後去分析這個功能點是如何實現的。但只要去追尋主體的實現邏輯即可,千萬不要試圖去搞懂每一行代碼都是什麼意思,那樣很容易會陷入到思維黑洞當中,而且越陷越深。因為這些龐大的系統都不是由一個人寫出來的,每一行代碼都想搞明白,就會感覺自己是在盲人摸象,永遠也研究不透。如果只是去分析主體的實現邏輯,那麼就有比較明確的目的性,這樣閱讀源碼會更加輕鬆,也更加有成效。

而今天帶大家閱讀的Glide源碼就非常適合使用這個技巧,因為Glide的源碼太複雜了,千萬不要試圖去搞明白它每行代碼的作用,而是應該只分析它的主體實現邏輯。那麼我們本篇文章就先確立好一個目標,就是要通過閱讀源碼搞明白下面這行代碼:

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

到底是如何實現將一張網路圖片展示到ImageView上面的。先將Glide的一整套圖片載入機制的基本流程梳理清楚,然後我們再通過後面的幾篇文章具體去了解Glide源碼方方面面的細節。

準備好了嗎?那麼我們現在開始。


源碼下載

既然是要閱讀Glide的源碼,那麼我們自然需要先將Glide的源碼下載下來。其實如果你是使用在build.gradle中添加依賴的方式將Glide引入到項目中的,那麼源碼自動就已經下載下來了,在Android Studio中就可以直接進行查看。

不過,使用添加依賴的方式引入的Glide,我們只能看到它的源碼,但不能做任何的修改,如果你還需要修改它的源碼的話,可以到GitHub上面將它的完整源碼下載下來。

Glide的GitHub主頁的地址是:https://github.com/bumptech/glide

不過在這個地址下載到的永遠都是最新的源碼,有可能還正在處於開發當中。而我們整個系列都是使用Glide 3.7.0這個版本來進行講解的,因此如果你需要專門去下載3.7.0版本的源碼,可以到這個地址進行下載:https://github.com/bumptech/glide/tree/v3.7.0


開始閱讀

我們在上一篇文章中已經學習過了,Glide最基本的用法就是三步走:先with(),再load(),最後into()。那麼我們開始一步步閱讀這三步走的源碼,先從with()看起。


with()方法是Glide類中的一組靜態方法,它有好幾個方法重載,我們來看一下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()方法重載的代碼都非常簡單,都是先調用RequestManagerRetriever的靜態get()方法得到一個RequestManagerRetriever對象,這個靜態get()方法就是一個單例實現,沒什麼需要解釋的。然後再調用RequestManagerRetriever的實例get()方法,去獲取RequestManager對象。

而RequestManagerRetriever的實例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

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

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;

}

...

}

上述代碼雖然看上去邏輯有點複雜,但是將它們梳理清楚後還是很簡單的。RequestManagerRetriever類中看似有很多個get()方法的重載,什麼Context參數,Activity參數,Fragment參數等等,實際上只有兩種情況而已,即傳入Application類型的參數,和傳入非Application類型的參數。

我們先來看傳入Application參數的情況。如果在Glide.with()方法中傳入的是一個Application對象,那麼這裡就會調用帶有Context參數的get()方法重載,然後會在第44行調用getApplicationManager()方法來獲取一個RequestManager對象。其實這是最簡單的一種情況,因為Application對象的生命周期即應用程序的生命周期,因此Glide並不需要做什麼特殊的處理,它自動就是和應用程序的生命周期是同步的,如果應用程序關閉的話,Glide的載入也會同時終止。

接下來我們看傳入非Application參數的情況。不管你在Glide.with()方法中傳入的是Activity、FragmentActivity、v4包下的Fragment、還是app包下的Fragment,最終的流程都是一樣的,那就是會向當前的Activity當中添加一個隱藏的Fragment。具體添加的邏輯是在上述代碼的第117行和第141行,分別對應的app包和v4包下的兩種Fragment的情況。那麼這裡為什麼要添加一個隱藏的Fragment呢?因為Glide需要知道載入的生命周期。很簡單的一個道理,如果你在某個Activity上正在載入著一張圖片,結果圖片還沒載入出來,Activity就被用戶關掉了,那麼圖片還應該繼續載入嗎?當然不應該。可是Glide並沒有辦法知道Activity的生命周期,於是Glide就使用了添加隱藏Fragment的這種小技巧,因為Fragment的生命周期和Activity是同步的,如果Activity被銷毀了,Fragment是可以監聽到的,這樣Glide就可以捕獲這個事件並停止圖片載入了。

這裡額外再提一句,從第48行代碼可以看出,如果我們是在非主線程當中使用的Glide,那麼不管你是傳入的Activity還是Fragment,都會被強制當成Application來處理。不過其實這就屬於是在分析代碼的細節了,本篇文章我們將會把目光主要放在Glide的主線工作流程上面,後面不會過多去分析這些細節方面的內容。

總體來說,第一個with()方法的源碼還是比較好理解的。其實就是為了得到一個RequestManager對象而已,然後Glide會根據我們傳入with()方法的參數來確定圖片載入的生命周期,並沒有什麼特別複雜的邏輯。不過複雜的邏輯還在後面等著我們呢,接下來我們開始分析第二步,load()方法。


由於with()方法返回的是一個RequestManager對象,那麼很容易就能想到,load()方法是在RequestManager類當中的,所以說我們首先要看的就是RequestManager這個類。不過在上一篇文章中我們學過,Glide是支持圖片URL字元串、圖片本地路徑等等載入形式的,因此RequestManager中也有很多個load()方法的重載。但是這裡我們不可能把每個load()方法的重載都看一遍,因此我們就只選其中一個載入圖片URL字元串的load()方法來進行研究吧。

RequestManager類的簡化代碼如下所示:

public class RequestManager implements LifecycleListener {

...

/**

* Returns a request builder to load the given {@link String}.

* signature.

*

* @see #fromString()

* @see #load(Object)

*

*/

public DrawableTypeRequest load(String string) {

return (DrawableTypeRequest) fromString().load(string);

}

/**

* Returns a request builder that loads data from {@link String}s using an empty signature.

*

* 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.

*

* @see #from(Class)

* @see #load(String)

*/

public DrawableTypeRequest fromString() {

return loadGeneric(String.class);

}

private DrawableTypeRequest loadGeneric(Class modelClass) {

ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);

ModelLoader 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(modelClass, streamModelLoader, fileDescriptorModelLoader, context,

glide, requestTracker, lifecycle, optionsApplier));

}

...

}

RequestManager類的代碼是非常多的,但是經過我這樣簡化之後,看上去就比較清爽了。在我們只探究載入圖片URL字元串這一個load()方法的情況下,那麼比較重要的方法就只剩下上述代碼中的這三個方法。

那麼我們先來看load()方法,這個方法中的邏輯是非常簡單的,只有一行代碼,就是先調用了fromString()方法,再調用load()方法,然後把傳入的圖片URL地址傳進去。而fromString()方法也極為簡單,就是調用了loadGeneric()方法,並且指定參數為String.class,因為load()方法傳入的是一個字元串參數。那麼看上去,好像主要的工作都是在loadGeneric()方法中進行的了。

其實loadGeneric()方法也沒幾行代碼,這裡分別調用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法來獲得ModelLoader對象。ModelLoader對象是用於載入圖片的,而我們給load()方法傳入不同類型的參數,這裡也會得到不同的ModelLoader對象。不過buildStreamModelLoader()方法內部的邏輯還是蠻複雜的,這裡就不展開介紹了,要不然篇幅實在收不住,感興趣的話你可以自己研究。由於我們剛才傳入的參數是String.class,因此最終得到的是StreamStringLoader對象,它是實現了ModelLoader介面的。

最後我們可以看到,loadGeneric()方法是要返回一個DrawableTypeRequest對象的,因此在loadGeneric()方法的最後又去new了一個DrawableTypeRequest對象,然後把剛才獲得的ModelLoader對象,還有一大堆雜七雜八的東西都傳了進去。具體每個參數的含義和作用就不解釋了,我們只看主線流程。

那麼這個DrawableTypeRequest的作用是什麼呢?我們來看下它的源碼,如下所示:

public class DrawableTypeRequest extends DrawableRequestBuilder implements DownloadOptions {

private final ModelLoader streamModelLoader;

private final ModelLoader fileDescriptorModelLoader;

private final RequestManager.OptionsApplier optionsApplier;

private static FixedLoadProvider buildProvider(Glide glide,

ModelLoader streamModelLoader,

ModelLoader fileDescriptorModelLoader, Class resourceClass,

Class transcodedClass,

ResourceTranscoder transcoder) {

if (streamModelLoader == null && fileDescriptorModelLoader == null) {

return null;

}

if (transcoder == null) {

transcoder = glide.buildTranscoder(resourceClass, transcodedClass);

}

DataLoadProvider dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,

resourceClass);

ImageVideoModelLoader modelLoader = new ImageVideoModelLoader(streamModelLoader,

fileDescriptorModelLoader);

return new FixedLoadProvider(modelLoader, transcoder, dataLoadProvider);

}

DrawableTypeRequest(Class modelClass, ModelLoader streamModelLoader,

ModelLoader 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 asBitmap() {

return optionsApplier.apply(new BitmapTypeRequest(this, streamModelLoader,

fileDescriptorModelLoader, optionsApplier));

}

/**

* 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

*

*/

public GifTypeRequest asGif() {

return optionsApplier.apply(new GifTypeRequest(this, streamModelLoader, optionsApplier));

}

...

}

這個類中的代碼本身就不多,我只是稍微做了一點簡化。可以看到,最主要的就是它提供了asBitmap()和asGif()這兩個方法。這兩個方法我們在上一篇文章當中都是學過的,分別是用於強制指定載入靜態圖片和動態圖片。而從源碼中可以看出,它們分別又創建了一個BitmapTypeRequest和GifTypeRequest,如果沒有進行強制指定的話,那默認就是使用DrawableTypeRequest。

好的,那麼我們再回到RequestManager的load()方法中。剛才已經分析過了,fromString()方法會返回一個DrawableTypeRequest對象,接下來會調用這個對象的load()方法,把圖片的URL地址傳進去。但是我們剛才看到了,DrawableTypeRequest中並沒有load()方法,那麼很容易就能猜想到,load()方法是在父類當中的。

DrawableTypeRequest的父類是DrawableRequestBuilder,我們來看下這個類的源碼:

public class DrawableRequestBuilder

extends GenericRequestBuilder

implements BitmapOptions, DrawableOptions {

DrawableRequestBuilder(Context context, Class modelClass,

LoadProvider loadProvider, Glide glide,

RequestTracker requestTracker, Lifecycle lifecycle) {

super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);

// Default to animating.

crossFade();

}

public DrawableRequestBuilder thumbnail(

DrawableRequestBuilder thumbnailRequest) {

super.thumbnail(thumbnailRequest);

return this;

}

@Override

public DrawableRequestBuilder thumbnail(

GenericRequestBuilder thumbnailRequest) {

super.thumbnail(thumbnailRequest);

return this;

}

@Override

public DrawableRequestBuilder thumbnail(float sizeMultiplier) {

super.thumbnail(sizeMultiplier);

return this;

}

@Override

public DrawableRequestBuilder sizeMultiplier(float sizeMultiplier) {

super.sizeMultiplier(sizeMultiplier);

return this;

}

@Override

public DrawableRequestBuilder decoder(ResourceDecoder decoder) {

super.decoder(decoder);

return this;

}

@Override

public DrawableRequestBuilder cacheDecoder(ResourceDecoder cacheDecoder) {

super.cacheDecoder(cacheDecoder);

return this;

}

@Override

public DrawableRequestBuilder encoder(ResourceEncoder encoder) {

super.encoder(encoder);

return this;

}

@Override

public DrawableRequestBuilder priority(Priority priority) {

super.priority(priority);

return this;

}

public DrawableRequestBuilder transform(BitmapTransformation... transformations) {

return bitmapTransform(transformations);

}

public DrawableRequestBuilder centerCrop() {

return transform(glide.getDrawableCenterCrop());

}

public DrawableRequestBuilder fitCenter() {

return transform(glide.getDrawableFitCenter());

}

public DrawableRequestBuilder bitmapTransform(Transformation... bitmapTransformations) {

GifBitmapWrapperTransformation[] transformations =

new GifBitmapWrapperTransformation[bitmapTransformations.length];

for (int i = 0; i

transformations[i] = new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);

}

return transform(transformations);

}

@Override

public DrawableRequestBuilder transform(Transformation... transformation) {

super.transform(transformation);

return this;

}

@Override

public DrawableRequestBuilder transcoder(

ResourceTranscoder transcoder) {

super.transcoder(transcoder);

return this;

}

public final DrawableRequestBuilder crossFade() {

super.animate(new DrawableCrossFadeFactory());

return this;

}

public DrawableRequestBuilder crossFade(int duration) {

super.animate(new DrawableCrossFadeFactory(duration));

return this;

}

public DrawableRequestBuilder crossFade(int animationId, int duration) {

super.animate(new DrawableCrossFadeFactory(context, animationId,

duration));

return this;

}

@Override

public DrawableRequestBuilder dontAnimate() {

super.dontAnimate();

return this;

}

@Override

public DrawableRequestBuilder animate(ViewPropertyAnimation.Animator animator) {

super.animate(animator);

return this;

}

@Override

public DrawableRequestBuilder animate(int animationId) {

super.animate(animationId);

return this;

}

@Override

public DrawableRequestBuilder placeholder(int resourceId) {

super.placeholder(resourceId);

return this;

}

@Override

public DrawableRequestBuilder placeholder(Drawable drawable) {

super.placeholder(drawable);

return this;

}

@Override

public DrawableRequestBuilder fallback(Drawable drawable) {

super.fallback(drawable);

return this;

}

@Override

public DrawableRequestBuilder fallback(int resourceId) {

super.fallback(resourceId);

return this;

}

@Override

public DrawableRequestBuilder error(int resourceId) {

super.error(resourceId);

return this;

}

@Override

public DrawableRequestBuilder error(Drawable drawable) {

super.error(drawable);

return this;

}

@Override

public DrawableRequestBuilder listener(

RequestListener requestListener) {

super.listener(requestListener);

return this;

}

@Override

public DrawableRequestBuilder diskCacheStrategy(DiskCacheStrategy strategy) {

super.diskCacheStrategy(strategy);

return this;

}

@Override

public DrawableRequestBuilder skipMemoryCache(boolean skip) {

super.skipMemoryCache(skip);

return this;

}

@Override

public DrawableRequestBuilder override(int width, int height) {

super.override(width, height);

return this;

}

@Override

public DrawableRequestBuilder sourceEncoder(Encoder sourceEncoder) {

super.sourceEncoder(sourceEncoder);

return this;

}

@Override

public DrawableRequestBuilder dontTransform() {

super.dontTransform();

return this;

}

@Override

public DrawableRequestBuilder signature(Key signature) {

super.signature(signature);

return this;

}

@Override

public DrawableRequestBuilder load(ModelType model) {

super.load(model);

return this;

}

@Override

public DrawableRequestBuilder clone() {

return (DrawableRequestBuilder) super.clone();

}

@Override

public Target into(ImageView view) {

return super.into(view);

}

@Override

void applyFitCenter() {

fitCenter();

}

@Override

void applyCenterCrop() {

centerCrop();

}

}

DrawableRequestBuilder中有很多個方法,這些方法其實就是Glide絕大多數的API了。裡面有不少我們在上篇文章中已經用過了,比如說placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等。當然還有很多暫時還沒用到的API,我們會在後面的文章當中學習。

到這裡,第二步load()方法也就分析結束了。為什麼呢?因為你會發現DrawableRequestBuilder類中有一個into()方法(上述代碼第220行),也就是說,最終load()方法返回的其實就是一個DrawableTypeRequest對象。那麼接下來我們就要進行第三步了,分析into()方法中的邏輯。


如果說前面兩步都是在準備開胃小菜的話,那麼現在終於要進入主菜了,因為into()方法也是整個Glide圖片載入流程中邏輯最複雜的地方。

不過從剛才的代碼來看,into()方法中並沒有任何邏輯,只有一句super.into(view)。那麼很顯然,into()方法的具體邏輯都是在DrawableRequestBuilder的父類當中了。

DrawableRequestBuilder的父類是GenericRequestBuilder,我們來看一下GenericRequestBuilder類中的into()方法,如下所示:

public Target 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));

}

這裡我們還是只抓核心代碼,其實只有兩行是最關鍵的,第15行調用buildRequest()方法構建出了一個Request對象,還有第18行來執行這個Request。

Request是用來發出載入圖片請求的,它是Glide中非常關鍵的一個組件。我們先來看buildRequest()方法是如何構建Request對象的:

private Request buildRequest(Target target) {

if (priority == null) {

priority = Priority.NORMAL;

}

return buildRequestRecursive(target, null);

}

private Request buildRequestRecursive(Target 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 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()方法的內部其實又調用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代碼雖然有點長,但是其中90%的代碼都是在處理縮略圖的。如果我們只追主線流程的話,那麼只需要看第47行代碼就可以了。這裡調用了obtainRequest()方法來獲取一個Request對象,而obtainRequest()方法中又去調用了GenericRequest的obtain()方法。注意這個obtain()方法需要傳入非常多的參數,而其中很多的參數我們都是比較熟悉的,像什麼placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我們就有理由猜測,剛才在load()方法中調用的所有API,其實都是在這裡組裝到Request對象當中的。那麼我們進入到這個GenericRequest的obtain()方法瞧一瞧:

public final class GenericRequest implements Request, SizeReadyCallback,

ResourceCallback {

...

public static GenericRequest obtain(

LoadProvider loadProvider,

A model,

Key signature,

Context context,

Priority priority,

Target target,

float sizeMultiplier,

Drawable placeholderDrawable,

int placeholderResourceId,

Drawable errorDrawable,

int errorResourceId,

Drawable fallbackDrawable,

int fallbackResourceId,

RequestListener requestListener,

RequestCoordinator requestCoordinator,

Engine engine,

Transformation transformation,

Class transcodeClass,

boolean isMemoryCacheable,

GlideAnimationFactory animationFactory,

int overrideWidth,

int overrideHeight,

DiskCacheStrategy diskCacheStrategy) {

@SuppressWarnings("unchecked")

GenericRequest request = (GenericRequest) REQUEST_POOL.poll();

if (request == null) {

request = new GenericRequest();

}

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()方法實際上獲得的就是一個GenericRequest對象。另外這裡又在第35行調用了GenericRequest的init(),裡面主要就是一些賦值的代碼,將傳入的這些參數賦值到GenericRequest的成員變數當中,我們就不再跟進去看了。

好,那現在解決了構建Request對象的問題,接下來我們看一下這個Request對象又是怎麼執行的。回到剛才的into()方法,你會發現在第18行調用了requestTracker.runRequest()方法來去執行這個Request,那麼我們跟進去瞧一瞧,如下所示:

/**

* Starts tracking the given request.

*/

public void runRequest(Request request) {

requests.add(request);

if (!isPaused) {

request.begin();

} else {

pendingRequests.add(request);

}

}

這裡有一個簡單的邏輯判斷,就是先判斷Glide當前是不是處理暫停狀態,如果不是暫停狀態就調用Request的begin()方法來執行Request,否則的話就先將Request添加到待執行隊列裡面,等暫停狀態解除了之後再執行。

暫停請求的功能仍然不是這篇文章所關心的,這裡就直接忽略了,我們重點來看這個begin()方法。由於當前的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));

}

}

這裡我們來注意幾個細節,首先如果model等於null,model也就是我們在第二步load()方法中傳入的圖片URL地址,這個時候會調用onException()方法。如果你跟到onException()方法裡面去看看,你會發現它最終會調用到一個setErrorPlaceholder()當中,如下所示:

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占點陣圖,然後調用target.onLoadFailed()方法並將佔點陣圖傳入。那麼onLoadFailed()方法中做了什麼呢?我們看一下:

public abstract class ImageViewTarget extends ViewTarget implements GlideAnimation.ViewAdapter {

...

@Override

public void onLoadStarted(Drawable placeholder) {

view.setImageDrawable(placeholder);

}

@Override

public void onLoadFailed(Exception e, Drawable errorDrawable) {

view.setImageDrawable(errorDrawable);

}

...

}

很簡單,其實就是將這張error占點陣圖顯示到ImageView上而已,因為現在出現了異常,沒辦法展示正常的圖片了。而如果你仔細看下剛才begin()方法的第15行,你會發現它又調用了一個target.onLoadStarted()方法,並傳入了一個loading占點陣圖,在也就說,在圖片請求開始之前,會先使用這張占點陣圖代替最終的圖片顯示。這也是我們在上一篇文章中學過的placeholder()和error()這兩個占點陣圖API底層的實現原理。

好,那麼我們繼續回到begin()方法。剛才講了占點陣圖的實現,那麼具體的圖片載入又是從哪裡開始的呢?是在begin()方法的第10行和第12行。這裡要分兩種情況,一種是你使用了override() API為圖片指定了一個固定的寬高,一種是沒有指定。如果指定了的話,就會執行第10行代碼,調用onSizeReady()方法。如果沒指定的話,就會執行第12行代碼,調用target.getSize()方法。這個target.getSize()方法的內部會根據ImageView的layout_width和layout_height值做一系列的計算,來算出圖片應該的寬高。具體的計算細節我就不帶著大家分析了,總之在計算完之後,它也會調用onSizeReady()方法。也就是說,不管是哪種情況,最終都會調用到onSizeReady()方法,在這裡進行下一步操作。那麼我們跟到這個方法裡面來:

@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 modelLoader = loadProvider.getModelLoader();

final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);

if (dataFetcher == null) {

onException(new Exception("Failed to load model: "" + model + """));

return;

}

ResourceTranscoder 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));

}

}

從這裡開始,真正複雜的地方來了,我們需要慢慢進行分析。先來看一下,在第12行調用了loadProvider.getModelLoader()方法,那麼我們第一個要搞清楚的就是,這個loadProvider是什麼?要搞清楚這點,需要先回到第二步的load()方法當中。還記得load()方法是返回一個DrawableTypeRequest對象嗎?剛才我們只是分析了DrawableTypeRequest當中的asBitmap()和asGif()方法,並沒有仔細看它的構造函數,現在我們重新來看一下DrawableTypeRequest類的構造函數:

public class DrawableTypeRequest extends DrawableRequestBuilder implements DownloadOptions {

private final ModelLoader streamModelLoader;

private final ModelLoader fileDescriptorModelLoader;

private final RequestManager.OptionsApplier optionsApplier;

private static FixedLoadProvider buildProvider(Glide glide,

ModelLoader streamModelLoader,

ModelLoader fileDescriptorModelLoader, Class resourceClass,

Class transcodedClass,

ResourceTranscoder transcoder) {

if (streamModelLoader == null && fileDescriptorModelLoader == null) {

return null;

}

if (transcoder == null) {

transcoder = glide.buildTranscoder(resourceClass, transcodedClass);

}

DataLoadProvider dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,

resourceClass);

ImageVideoModelLoader modelLoader = new ImageVideoModelLoader(streamModelLoader,

fileDescriptorModelLoader);

return new FixedLoadProvider(modelLoader, transcoder, dataLoadProvider);

}

DrawableTypeRequest(Class modelClass, ModelLoader streamModelLoader,

ModelLoader 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行,也就是構造函數中,調用了一個buildProvider()方法,並把streamModelLoader和fileDescriptorModelLoader等參數傳入到這個方法中,這兩個ModelLoader就是之前在loadGeneric()方法中構建出來的。

那麼我們再來看一下buildProvider()方法裡面做了什麼,在第16行調用了glide.buildTranscoder()方法來構建一個ResourceTranscoder,它是用於對圖片進行轉碼的,由於ResourceTranscoder是一個介面,這裡實際會構建出一個GifBitmapWrapperDrawableTranscoder對象。

接下來在第18行調用了glide.buildDataProvider()方法來構建一個DataLoadProvider,它是用於對圖片進行編解碼的,由於DataLoadProvider是一個介面,這裡實際會構建出一個ImageVideoGifDrawableLoadProvider對象。

然後在第20行,new了一個ImageVideoModelLoader的實例,並把之前loadGeneric()方法中構建的兩個ModelLoader封裝到了ImageVideoModelLoader當中。

最後,在第22行,new出一個FixedLoadProvider,並把剛才構建的出來的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封裝進去,這個也就是onSizeReady()方法中的loadProvider了。

好的,那麼我們回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行,分別調用了loadProvider的getModelLoader()方法和getTranscoder()方法,那麼得到的對象也就是剛才我們分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。而在第13行,又調用了ImageVideoModelLoader的getResourceFetcher()方法,這裡我們又需要跟進去瞧一瞧了,代碼如下所示:

public class ImageVideoModelLoader implements ModelLoader {

private static final String TAG = "IVML";

private final ModelLoader streamLoader;

private final ModelLoader fileDescriptorLoader;

public ImageVideoModelLoader(ModelLoader streamLoader,

ModelLoader 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 getResourceFetcher(A model, int width, int height) {

DataFetcher streamFetcher = null;

if (streamLoader != null) {

streamFetcher = streamLoader.getResourceFetcher(model, width, height);

}

DataFetcher

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 {

private final DataFetcher streamFetcher;

private final DataFetcher

fileDescriptorFetcher;

public ImageVideoFetcher(DataFetcher streamFetcher,

DataFetcher

fileDescriptorFetcher) {

this.streamFetcher = streamFetcher;

this.fileDescriptorFetcher = fileDescriptorFetcher;

}

...

}

}

可以看到,在第20行會先調用streamLoader.getResourceFetcher()方法獲取一個DataFetcher,而這個streamLoader其實就是我們在loadGeneric()方法中構建出的StreamStringLoader,調用它的getResourceFetcher()方法會得到一個HttpUrlFetcher對象。然後在第28行new出了一個ImageVideoFetcher對象,並把獲得的HttpUrlFetcher對象傳進去。也就是說,ImageVideoModelLoader的getResourceFetcher()方法得到的是一個ImageVideoFetcher。

那麼我們再次回到onSizeReady()方法,在onSizeReady()方法的第23行,這裡將剛才獲得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起傳入到了Engine的load()方法當中。接下來我們就要看一看,這個Engine的load()方法當中,到底做了什麼?代碼如下所示:

public class Engine implements EngineJobListener,

MemoryCache.ResourceRemovedListener,

EngineResource.ResourceListener {

...

public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,

DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder 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 decodeJob = new DecodeJob(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()方法中的代碼雖然有點長,但大多數的代碼都是在處理緩存的。關於緩存的內容我們會在下一篇文章當中學習,現在只需要從第45行看起就行。這裡構建了一個EngineJob,它的主要作用就是用來開啟線程的,為後面的非同步載入圖片做準備。接下來第46行創建了一個DecodeJob對象,從名字上來看,它好像是用來對圖片進行解碼的,但實際上它的任務十分繁重,待會我們就知道了。繼續往下看,第48行創建了一個EngineRunnable對象,並且在51行調用了EngineJob的start()方法來運行EngineRunnable對象,這實際上就是讓EngineRunnable的run()方法在子線程當中執行了。那麼我們現在就可以去看看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);

}

}

這個方法中的代碼並不多,但我們仍然還是要抓重點。在第9行,這裡調用了一個decode()方法,並且這個方法返回了一個Resource對象。看上去所有的邏輯應該都在這個decode()方法執行的了,那我們跟進去瞧一瞧:

private Resource decode() throws Exception {

if (isDecodingFromCache()) {

return decodeFromCache();

} else {

return decodeFromSource();

}

}

decode()方法中又分了兩種情況,從緩存當中去decode圖片的話就會執行decodeFromCache(),否則的話就執行decodeFromSource()。本篇文章中我們不討論緩存的情況,那麼就直接來看decodeFromSource()方法的代碼吧,如下所示:

private Resource decodeFromSource() throws Exception {

return decodeJob.decodeFromSource();

}

這裡又調用了DecodeJob的decodeFromSource()方法。剛才已經說了,DecodeJob的任務十分繁重,我們繼續跟進看一看吧:

class DecodeJob {

...

public Resource decodeFromSource() throws Exception {

Resource decoded = decodeSource();

return transformEncodeAndTranscode(decoded);

}

private Resource decodeSource() throws Exception {

Resource 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()方法,其實它的工作分為兩部,第一步是調用decodeSource()方法來獲得一個Resource對象,第二步是調用transformEncodeAndTranscode()方法來處理這個Resource對象。

那麼我們先來看第一步,decodeSource()方法中的邏輯也並不複雜,首先在第14行調用了fetcher.loadData()方法。那麼這個fetcher是什麼呢?其實就是剛才在onSizeReady()方法中得到的ImageVideoFetcher對象,這裡調用它的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行,這裡又去調用了streamFetcher.loadData()方法,那麼這個streamFetcher是什麼呢?自然就是剛才在組裝ImageVideoFetcher對象時傳進來的HttpUrlFetcher了。因此這裡又會去調用HttpUrlFetcher的loadData()方法,那麼我們繼續跟進去瞧一瞧:

public class HttpUrlFetcher implements DataFetcher {

...

@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 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 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;

}

...

}

經過一層一層地跋山涉水,我們終於在這裡找到網路通訊的代碼了!之前有朋友跟我講過,說Glide的源碼實在是太複雜了,甚至連網路請求是在哪裡發出去的都找不到。我們也是經過一段一段又一段的代碼跟蹤,終於把網路請求的代碼給找出來了,實在是太不容易了。

不過也別高興得太早,現在離最終分析完還早著呢。可以看到,loadData()方法只是返回了一個InputStream,伺服器返回的數據連讀都還沒開始讀呢。所以我們還是要靜下心來繼續分析,回到剛才ImageVideoFetcher的loadData()方法中,在這個方法的最後一行,創建了一個ImageVideoWrapper對象,並把剛才得到的InputStream作為參數傳了進去。

然後我們回到再上一層,也就是DecodeJob的decodeSource()方法當中,在得到了這個ImageVideoWrapper對象之後,緊接著又將這個對象傳入到了decodeFromSourceData()當中,來去解碼這個對象。decodeFromSourceData()方法的代碼如下所示:

private Resource decodeFromSourceData(A data) throws IOException {

final Resource 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行調用了loadProvider.getSourceDecoder().decode()方法來進行解碼。loadProvider就是剛才在onSizeReady()方法中得到的FixedLoadProvider,而getSourceDecoder()得到的則是一個GifBitmapWrapperResourceDecoder對象,也就是要調用這個對象的decode()方法來對圖片進行解碼。那麼我們來看下GifBitmapWrapperResourceDecoder的代碼:

public class GifBitmapWrapperResourceDecoder implements ResourceDecoder {

...

@SuppressWarnings("resource")

// @see ResourceDecoder.decode

@Override

public Resource 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 bitmapResource = bitmapDecoder.decode(toDecode, width, height);

if (bitmapResource != null) {

result = new GifBitmapWrapper(bitmapResource, null);

}

return result;

}

...

}

首先,在decode()方法中,又去調用了另外一個decode()方法的重載。然後在第23行調用了decodeStream()方法,準備從伺服器返回的流當中讀取數據。decodeStream()方法中會先從流中讀取2個位元組的數據,來判斷這張圖是GIF圖還是普通的靜圖,如果是GIF圖就調用decodeGifWrapper()方法來進行解碼,如果是普通的靜圖就用調用decodeBitmapWrapper()方法來進行解碼。這裡我們只分析普通靜圖的實現流程,GIF圖的實現有點過於複雜了,無法在本篇文章當中分析。

然後我們來看一下decodeBitmapWrapper()方法,這裡在第52行調用了bitmapDecoder.decode()方法。這個bitmapDecoder是一個ImageVideoBitmapDecoder對象,那麼我們來看一下它的代碼,如下所示:

public class ImageVideoBitmapDecoder implements ResourceDecoder {

private final ResourceDecoder streamDecoder;

private final ResourceDecoder

fileDescriptorDecoder;

public ImageVideoBitmapDecoder(ResourceDecoder streamDecoder,

ResourceDecoder

fileDescriptorDecoder) {

this.streamDecoder = streamDecoder;

this.fileDescriptorDecoder = fileDescriptorDecoder;

}

@Override

public Resource decode(ImageVideoWrapper source, int width, int height) throws IOException {

Resource 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;

}

...

}

代碼並不複雜,在第14行先調用了source.getStream()來獲取到伺服器返回的InputStream,然後在第17行調用streamDecoder.decode()方法進行解碼。streamDecode是一個StreamBitmapDecoder對象,那麼我們再來看這個類的源碼,如下所示:

public class StreamBitmapDecoder implements ResourceDecoder {

...

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 decode(InputStream source, int width, int height) {

Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);

return BitmapResource.obtain(bitmap, bitmapPool);

}

...

}

可以看到,它的decode()方法又去調用了Downsampler的decode()方法。接下來又到了激動人心的時刻了,Downsampler的代碼如下所示:

public abstract class Downsampler implements BitmapDecoder {

...

@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

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 .

*/

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;

}

...

}

可以看到,對伺服器返回的InputStream的讀取,以及對圖片的載入全都在這裡了。當然這裡其實處理了很多的邏輯,包括對圖片的壓縮,甚至還有旋轉、圓角等邏輯處理,但是我們目前只需要關注主線邏輯就行了。decode()方法執行之後,會返回一個Bitmap對象,那麼圖片在這裡其實也就已經被載入出來了,剩下的工作就是如果讓這個Bitmap顯示到界面上,我們繼續往下分析。

回到剛才的StreamBitmapDecoder當中,你會發現,它的decode()方法返回的是一個Resource對象。而我們從Downsampler中得到的是一個Bitmap對象,因此這裡在第18行又調用了BitmapResource.obtain()方法,將Bitmap對象包裝成了Resource對象。代碼如下所示:

public class BitmapResource implements Resource {

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的源碼也非常簡單,經過這樣一層包裝之後,如果我還需要獲取Bitmap,只需要調用Resource的get()方法就可以了。

然後我們需要一層層繼續向上返回,StreamBitmapDecoder會將值返回到ImageVideoBitmapDecoder當中,而ImageVideoBitmapDecoder又會將值返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法當中。由於代碼隔得有點太遠了,我重新把decodeBitmapWrapper()方法的代碼貼一下:

private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {

GifBitmapWrapper result = null;

Resource bitmapResource = bitmapDecoder.decode(toDecode, width, height);

if (bitmapResource != null) {

result = new GifBitmapWrapper(bitmapResource, null);

}

return result;

}

可以看到,decodeBitmapWrapper()方法返回的是一個GifBitmapWrapper對象。因此,這裡在第5行,又將Resource封裝到了一個GifBitmapWrapper對象當中。這個GifBitmapWrapper顧名思義,就是既能封裝GIF,又能封裝Bitmap,從而保證了不管是什麼類型的圖片Glide都能從容應對。我們順便來看下GifBitmapWrapper的源碼吧,如下所示:

public class GifBitmapWrapper {

private final Resource gifResource;

private final Resource bitmapResource;

public GifBitmapWrapper(Resource bitmapResource, Resource 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 getBitmapResource() {

return bitmapResource;

}

/**

* Returns the wrapped {@link GifDrawable} resource if it exists, or null.

*/

public Resource getGifResource() {

return gifResource;

}

}

還是比較簡單的,就是分別對gifResource和bitmapResource做了一層封裝而已,相信沒有什麼解釋的必要。

然後這個GifBitmapWrapper對象會一直向上返回,返回到GifBitmapWrapperResourceDecoder最外層的decode()方法的時候,會對它再做一次封裝,如下所示:

@Override

public Resource 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對象當中,最終返回的是一個Resource對象。這個GifBitmapWrapperResource和剛才的BitmapResource是相似的,它們都實現的Resource介面,都可以通過get()方法來獲取封裝起來的具體內容。GifBitmapWrapperResource的源碼如下所示:

public class GifBitmapWrapperResource implements Resource {

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 bitmapResource = data.getBitmapResource();

if (bitmapResource != null) {

bitmapResource.recycle();

}

Resource gifDataResource = data.getGifResource();

if (gifDataResource != null) {

gifDataResource.recycle();

}

}

}

經過這一層的封裝之後,我們從網路上得到的圖片就能夠以Resource介面的形式返回,並且還能同時處理Bitmap圖片和GIF圖片這兩種情況。

那麼現在我們可以回到DecodeJob當中了,它的decodeFromSourceData()方法返回的是一個Resource對象,其實也就是Resource對象了。然後繼續向上返回,最終返回到decodeFromSource()方法當中,如下所示:

public Resource decodeFromSource() throws Exception {

Resource decoded = decodeSource();

return transformEncodeAndTranscode(decoded);

}

剛才我們就是從這裡跟進到decodeSource()方法當中,然後執行了一大堆一大堆的邏輯,最終得到了這個Resource對象。然而你會發現,decodeFromSource()方法最終返回的卻是一個Resource對象,那麼這到底是怎麼回事呢?我們就需要跟進到transformEncodeAndTranscode()方法來瞧一瞧了,代碼如下所示:

private Resource transformEncodeAndTranscode(Resource decoded) {

long startTime = LogTime.getLogTime();

Resource transformed = transform(decoded);

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

logWithTimeAndKey("Transformed resource from source", startTime);

}

writeTransformedToCache(transformed);

startTime = LogTime.getLogTime();

Resource result = transcode(transformed);

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

logWithTimeAndKey("Transcoded transformed from source", startTime);

}

return result;

}

private Resource transcode(Resource transformed) {

if (transformed == null) {

return null;

}

return transcoder.transcode(transformed);

}

首先,這個方法開頭的幾行transform還有cache,這都是我們後面才會學習的東西,現在不用管它們就可以了。需要注意的是第9行,這裡調用了一個transcode()方法,就把Resource對象轉換成Resource對象了。

而transcode()方法中又是調用了transcoder的transcode()方法,那麼這個transcoder是什麼呢?其實這也是Glide源碼特別難懂的原因之一,就是它用到的很多對象都是很早很早之前就初始化的,在初始化的時候你可能完全就沒有留意過它,因為一時半會根本就用不著,但是真正需要用到的時候你卻早就記不起來這個對象是從哪兒來的了。

那麼這裡我來提醒一下大家吧,在第二步load()方法返回的那個DrawableTypeRequest對象,它的構建函數中去構建了一個FixedLoadProvider對象,然後我們將三個參數傳入到了FixedLoadProvider當中,其中就有一個GifBitmapWrapperDrawableTranscoder對象。後來在onSizeReady()方法中獲取到了這個參數,並傳遞到了Engine當中,然後又由Engine傳遞到了DecodeJob當中。因此,這裡的transcoder其實就是這個GifBitmapWrapperDrawableTranscoder對象。那麼我們來看一下它的源碼:

public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder {

private final ResourceTranscoder bitmapDrawableResourceTranscoder;

public GifBitmapWrapperDrawableTranscoder(

ResourceTranscoder bitmapDrawableResourceTranscoder) {

this.bitmapDrawableResourceTranscoder = bitmapDrawableResourceTranscoder;

}

@Override

public Resource transcode(Resource toTranscode) {

GifBitmapWrapper gifBitmap = toTranscode.get();

Resource bitmapResource = gifBitmap.getBitmapResource();

final Resource result;

if (bitmapResource != null) {

result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);

} else {

result = gifBitmap.getGifResource();

}

return (Resource) result;

}

...

}

這裡我來簡單解釋一下,GifBitmapWrapperDrawableTranscoder的核心作用就是用來轉碼的。因為GifBitmapWrapper是無法直接顯示到ImageView上面的,只有Bitmap或者Drawable才能顯示到ImageView上。因此,這裡的transcode()方法先從Resource中取出GifBitmapWrapper對象,然後再從GifBitmapWrapper中取出Resource對象。

接下來做了一個判斷,如果Resource為空,那麼說明此時載入的是GIF圖,直接調用getGifResource()方法將圖片取出即可,因為Glide用於載入GIF圖片是使用的GifDrawable這個類,它本身就是一個Drawable對象了。而如果Resource不為空,那麼就需要再做一次轉碼,將Bitmap轉換成Drawable對象才行,因為要保證靜圖和動圖的類型一致性,不然邏輯上是不好處理的。

這裡在第15行又進行了一次轉碼,是調用的GlideBitmapDrawableTranscoder對象的transcode()方法,代碼如下所示:

public class GlideBitmapDrawableTranscoder implements ResourceTranscoder {

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 transcode(Resource toTranscode) {

GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());

return new GlideBitmapDrawableResource(drawable, bitmapPool);

}

...

}

可以看到,這裡new出了一個GlideBitmapDrawable對象,並把Bitmap封裝到裡面。然後對GlideBitmapDrawable再進行一次封裝,返回一個Resource對象。

現在再返回到GifBitmapWrapperDrawableTranscoder的transcode()方法中,你會發現它們的類型就一致了。因為不管是靜圖的Resource對象,還是動圖的Resource對象,它們都是屬於父類Resource對象的。因此transcode()方法也是直接返回了Resource,而這個Resource其實也就是轉換過後的Resource了。

那麼我們繼續回到DecodeJob當中,它的decodeFromSource()方法得到了Resource對象,當然也就是Resource對象。然後繼續向上返回會回到EngineRunnable的decodeFromSource()方法,再回到decode()方法,再回到run()方法當中。那麼我們重新再貼一下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);

}

}

也就是說,經過第9行decode()方法的執行,我們最終得到了這個Resource對象,那麼接下來就是如何將它顯示出來了。可以看到,這裡在第25行調用了onLoadComplete()方法,表示圖片載入已經完成了,代碼如下所示:

private void onLoadComplete(Resource resource) {

manager.onResourceReady(resource);

}

這個manager就是EngineJob對象,因此這裡實際上調用的是EngineJob的onResourceReady()方法,代碼如下所示:

class EngineJob implements EngineRunnable.EngineRunnableManager {

private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());

private final List cbs = new ArrayList();

...

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發出了一條MSG_COMPLETE消息,那麼在MainThreadCallback的handleMessage()方法中就會收到這條消息。從這裡開始,所有的邏輯又回到主線程當中進行了,因為很快就需要更新UI了。

然後在第72行調用了handleResultOnMainThread()方法,這個方法中又通過一個循環,調用了所有ResourceCallback的onResourceReady()方法。那麼這個ResourceCallback是什麼呢?答案在addCallback()方法當中,它會向cbs集合中去添加ResourceCallback。那麼這個addCallback()方法又是哪裡調用的呢?其實調用的地方我們早就已經看過了,只不過之前沒有注意,現在重新來看一下Engine的load()方法,如下所示:

public class Engine implements EngineJobListener,

MemoryCache.ResourceRemovedListener,

EngineResource.ResourceListener {

...

public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,

DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder, Priority priority,

boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {

...

EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);

DecodeJob decodeJob = new DecodeJob(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行上面,看到了嗎?就是在這裡調用的EngineJob的addCallback()方法來註冊的一個ResourceCallback。那麼接下來的問題就是,Engine.load()方法的ResourceCallback參數又是誰傳過來的呢?這就需要回到GenericRequest的onSizeReady()方法當中了,我們看到ResourceCallback是load()方法的最後一個參數,那麼在onSizeReady()方法中調用load()方法時傳入的最後一個參數是什麼?代碼如下所示:

public final class GenericRequest 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 modelLoader = loadProvider.getModelLoader();

final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);

if (dataFetcher == null) {

onException(new Exception("Failed to load model: "" + model + """));

return;

}

ResourceTranscoder 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行的最後一個參數,this。沒錯,就是this。GenericRequest本身就實現了ResourceCallback的介面,因此EngineJob的回調最終其實就是回調到了GenericRequest的onResourceReady()方法當中了,代碼如下所示:

public void onResourceReady(Resource resource) {

if (resource == null) {

onException(new Exception("Expected to receive a Resource 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 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()方法當中,調用resource.get()方法獲取到了封裝的圖片對象,也就是GlideBitmapDrawable對象,或者是GifDrawable對象。然後將這個值傳入到了第二個onResourceReady()方法當中,並在第36行調用了target.onResourceReady()方法。

那麼這個target又是什麼呢?這個又需要向上翻很久了,在第三步into()方法的一開始,我們就分析了在into()方法的最後一行,調用了glide.buildImageViewTarget()方法來構建出一個Target,而這個Target就是一個GlideDrawableImageViewTarget對象。

那麼我們去看GlideDrawableImageViewTarget的源碼就可以了,如下所示:

public class GlideDrawableImageViewTarget extends ImageViewTarget {

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 animation) {

if (!resource.isAnimated()) {

float viewRatio = view.getWidth() / (float) view.getHeight();

float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();

if (Math.abs(viewRatio - 1f)

&& Math.abs(drawableRatio - 1f)

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圖片的話,就調用resource.start()方法開始播放圖片,但是好像並沒有看到哪裡有將GlideDrawable顯示到ImageView上的邏輯。

確實沒有,不過父類裡面有,這裡在第25行調用了super.onResourceReady()方法,GlideDrawableImageViewTarget的父類是ImageViewTarget,我們來看下它的代碼吧:

public abstract class ImageViewTarget extends ViewTarget implements GlideAnimation.ViewAdapter {

...

@Override

public void onResourceReady(Z resource, GlideAnimation glideAnimation) {

if (glideAnimation == null || !glideAnimation.animate(resource, this)) {

setResource(resource);

}

}

protected abstract void setResource(Z resource);

}

可以看到,在ImageViewTarget的onResourceReady()方法當中調用了setResource()方法,而ImageViewTarget的setResource()方法是一個抽象方法,具體的實現還是在子類那邊實現的。

那子類的setResource()方法是怎麼實現的呢?回頭再來看一下GlideDrawableImageViewTarget的setResource()方法,沒錯,調用的view.setImageDrawable()方法,而這個view就是ImageView。代碼執行到這裡,圖片終於也就顯示出來了。

那麼,我們對Glide執行流程的源碼分析,到這裡也終於結束了。


總結

真是好長的一篇文章,這也可能是我目前所寫過的最長的一篇文章了。如果你之前沒有讀過Glide的源碼,真的很難相信,這短短一行代碼:

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

背後竟然蘊藏著如此極其複雜的邏輯吧?

不過Glide也並不是有意要將代碼寫得如此複雜,實在是因為Glide的功能太強大了,而上述代碼只是使用了Glide最最基本的功能而已。

現在通過兩篇文章,我們已經掌握了Glide的基本用法,並且通過閱讀源碼了解了Glide總的執行流程。接下來的幾篇文章,我會帶大家深入到Glide源碼的某一處細節,學習Glide更多的高級使用技巧,感興趣的朋友請繼續閱讀 Android圖片載入框架最全解析(三),深入探究Glide的緩存機制 。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 殘殤 的精彩文章:

Android跨進程IPC通信AIDL

TAG:殘殤 |