1. 程式人生 > >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()看起。

  1. 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 < 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;
    }

    ...
}

上述程式碼雖然看上去邏輯有點複雜,但是將它們梳理清楚後還是很簡單的。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()方法。

  1. 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)
     *
     * @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類的程式碼是非常多的,但是經過我這樣簡化之後,看上去就比較清爽了。在我們只探究載入圖片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<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));
    }

    ...
}

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

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

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

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

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

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

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的時候會再進行解釋,現在我們只需要關注最後一行程式碼。最後一行程式碼先是呼叫了glide.buildImageViewTarget()方法,這個方法會構建出一個Target物件,Target物件則是用來最終展示圖片用的,如果我們跟進去的話會看到如下程式碼:

<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}

這裡其實又是呼叫了ImageViewTargetFactory的buildTarget()方法,我們繼續跟進去,程式碼如下所示:

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()方法中會根據傳入的class引數來構建不同的Target物件。那如果你要分析這個class引數是從哪兒傳過來的,這可有得你分析了,簡單起見我直接幫大家梳理清楚。這個class引數其實基本上只有兩種情況,如果你在使用Glide載入圖片的時候呼叫了asBitmap()方法,那麼這裡就會構建出BitmapImageViewTarget物件,否則的話構建的都是GlideDrawableImageViewTarget物件。至於上述程式碼中的DrawableImageViewTarget物件,這個通常都是用不到的,我們可以暫時不用管它。

也就是說,通過glide.buildImageViewTarget()方法,我們構建出了一個GlideDrawableImageViewTarget物件。那現在回到剛才into()方法的最後一行,可以看到,這裡又將這個引數傳入到了GenericRequestBuilder另一個接收Target物件的into()方法當中了。我們來看一下這個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;
}

這裡我們還是隻抓核心程式碼,其實只有兩行是最關鍵的,第15行呼叫buildRequest()方法構建出了一個Request物件,還有第18行來執行這個Request。

Request是用來發出載入圖片請求的,它是Glide中非常關鍵的一個元件。我們先來看buildRequest()方法是如何構建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()方法的內部其實又呼叫了buildRequestRecursive()方法,而buildRequestRecursive()方法中的程式碼雖然有點長,但是其中90%的程式碼都是在處理縮圖的。如果我們只追主線流程的話,那麼只需要看第47行程式碼就可以了。這裡呼叫了obtainRequest()方法來獲取一個Request物件,而obtainRequest()方法中又去呼叫了GenericRequest的obtain()方法。注意這個obtain()方法需要傳入非常多的引數,而其中很多的引數我們都是比較熟悉的,像什麼placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我們就有理由猜測,剛才在load()方法中呼叫的所有API,其實都是在這裡組裝到Request物件當中的。那麼我們進入到這個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()方法實際上獲得的就是一個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<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);
    }

    ...
}

很簡單,其實就是將這張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<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));
    }
}

從這裡開始,真正複雜的地方來了,我們需要慢慢進行分析。先來看一下,在第12行呼叫了loadProvider.getModelLoader()方法,那麼我們第一個要搞清楚的就是,這個loadProvider是什麼?要搞清楚這點,需要先回到第二步的load()方法當中。還記得load()方法是返回一個DrawableTypeRequest物件嗎?剛才我們只是分析了DrawableTypeRequest當中的asBitmap()和asGif()方法,並沒有仔細看它的建構函式,現在我們重新來看一下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;
    }

    ...
}

可以看到,這裡在第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<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行會先呼叫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 <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()方法中的程式碼雖然有點長,但大多數的程式碼都是在處理快取的。關於快取的內容我們會在下一篇文章當中學習,現在只需要從第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<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;
            }