Glide源碼分析

思惟導圖:Glide源碼的流程

(流程圖可能有點怪,最後有個水平流程圖,不過可能看不清楚)android



1、Glide簡介

Glide是一款由Bump Technologies開發的圖片加載框架,使得咱們能夠在Android平臺上以季度簡單的方式加載和展現圖片。git

其做用:github

  • GIF動畫的解碼
  • 本地視頻劇照的解碼
  • 縮略圖的支持
  • Activity生命週期的集成
  • 轉碼的支持
  • 動畫的支持
  • OkHttp和Volley的支持
  • 其餘功能:如在圖片加載過程當中,使用Drawables對象做爲佔位符、圖片請求的優化、圖片的寬度和高度可從新設定、縮略圖和原圖的緩存等功能


2、用法

一、添加一個依賴

在app/build.gradle文件中添加依賴緩存

compile 'com.github.bumptech.glide:glide:3.7.0'
複製代碼

二、加載圖片的核心代碼

從網絡請求一張圖片,放入控件ImageView中bash

Glide.with(this)
     .load(url)  //url:網絡請求圖片的連接地址
     .into(show);//show = (ImageView) findViewById(R.id.image_view);複製代碼

在上述代碼中:markdown

  • with(context):這個方法時建立一個加載圖片的實例,能夠接受不少參數,不管是在一個Activity仍是在Fragment裏面,均可以
  • load(url)指定加載的圖片資源,各類方式的
  • into(target):這個是最好理解的,由於它指定要放在哪一個ImageView裏面,還支持不少用法

三、佔位圖和加載失敗的圖片

有時咱們發現這個仍是不知足咱們的需求,由於還須要如下兩種狀況下的圖片:網絡

  • 加載時間太長怎麼辦,不能讓別人看到空白
  • 加載失敗怎麼辦,也不可能一大堆空白

可是這個框架已經幫咱們解決了。app

首先先看下佔位圖框架

佔位圖就是指在圖片的加載過程當中,咱們先顯示一張臨時的圖片,等圖片加載出來了再替換成要加載的圖片。ide

代碼以下:

Glide.with(this)
     .load(url)  //url:網絡請求圖片的連接地址
     .placeholder(R.drawable.noimage) //加入佔位圖
     .into(show);//show = (ImageView) findViewById(R.id.image_view);複製代碼

而後咱們再看看加載失敗的狀況下的處理:

異常佔位圖

若是由於某些異常狀況致使圖片加載失敗,好比手機信號很差,這是就顯示一張異常佔位圖。

用法也是同樣,以下:

Glide.with(this)
     .load(url)  //url:網絡請求圖片的連接地址
     .placeholder(R.drawable.noimage) //加入佔位圖
     .error(R.drawable.errorimage) //異常處理的圖片方法
     .into(show);//show = (ImageView) findViewById(R.id.image_view);複製代碼


3、源碼分析

一、with(context)方法

看一下with(context)的源碼:

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);
    }
    
    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()方法有不少,但內容基本一致,都是經過RequestManagerRetriever.get()獲取RequestManagerRetriever對象retriever,而後經過retriever.get(context)獲取一個RequestManager對象並返回。

這些with()方法關鍵的不一樣在於傳入的參數不一樣,能夠是ContextActivityFragment等等。

那麼爲何要分這麼多種呢?其實咱們應該知道:Glide加載圖片的時候要綁定with(context)方法中傳入的context的生命週期,若是傳入的是Activity,那麼在這個Activity銷燬的時候Glide會中止圖片的加載。這樣作的好處在於避免了消耗多餘的資源,也避免了在Activity銷燬以後加載圖片從而致使空指針問題


爲了更好的分析with(context)中的這兩步,咱們來看一下RequestManagerRetriever類:

public class RequestManagerRetriever implements Handler.Callback {
    //餓漢式建立單例
    private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
    
    //返回單例對象
    public static RequestManagerRetriever get() {
        return INSTANCE;
    }

    //根據傳入的參數,獲取不一樣的RequestManager
    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);
    }
    
    //省略無關代碼......
}複製代碼

很明顯,這是個餓漢式單例模式,關鍵在於retriever.get(context),咱們繼續看代碼:

//根據傳入的參數,獲取不一樣的RequestManager
    public RequestManager get(Context context) {
        //context爲null則拋出異常
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (Util.isOnMainThread() && !(context instanceof Application)) {
            //當前線程是主線程而且此context並非Application的實例,
            //根據context的類型作不一樣的處理
            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);
    }複製代碼

上面這個方法主要是經過傳入context不一樣類型來作不一樣的操做。context能夠是ApplicationFragmentActivityActivity或者是ContextWrapper

咱們先看一下當contextApplication時的操做:

private RequestManager getApplicationManager(Context context) {
        // 返回一個單例
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }

        return applicationManager;
    }複製代碼

getApplicationManager(Context context)經過雙檢查單例模式建立並返回applicationManager

咱們再來看下若是傳入的contextActivity時的操做:

public RequestManager get(Activity activity) {
        //若是不在主線程或者Android SDK的版本低於HONEYCOMB,傳入的仍是Application類型的context
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return get(activity.getApplicationContext());
        } else {
            //判斷當前activity是否被銷燬
            assertNotDestroyed(activity);
            android.app.FragmentManager fm = activity.getFragmentManager();
           //經過fragmentGet(activity, fm)獲取RequestManager
            return fragmentGet(activity, fm);
        }
    }複製代碼

該代碼邏輯很簡單:若是不在主線程或者Android SDK版本太低,走的仍是傳入Application的方法,這個方法在上面提到過;反之,首先判斷當前activity是否被銷燬,若沒有被銷燬,則經過fragmentGet(activity, fm)獲取RequestManager

關鍵是這個fragmentGet(activity, fm),咱們看下源碼:

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        //在當前activity中建立一個沒有界面的的fragment並add到當前activity中
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            //建立一個requestManager
            requestManager = new RequestManager(context, current.getLifecycle(), 
                       current.getRequestManagerTreeNode());
            //將requestManager與fragment綁定        
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }複製代碼

fragmentGet()這個方法主要是在當前activity中建立一個沒有界面的fragment並add到當前activity中,以此來實現對activity生命週期的監聽。到此,with()方法已經基本介紹完畢。


二、with()方法總結

  • 經過RequestManagerRetriever.get()獲取RequestManagerRetriever單例對象
  • 經過retriever.get(context)獲取RequestManager,在get(context)方法中經過對context類型的判斷作不一樣的處理
  • context是Application,經過getApplicationManager(Context context)建立並返回一個RequestManager對象
  • context是Activity,經過fragmentGet(activity, fm)在當前activity建立並添加一個沒有界面fragment,從而實現圖片加載與activity的生命週期相綁定,以後建立並返回一個RequestManager對象

三、load(url)

with(context)返回一個RequestManager,接下來咱們再看看RequestManager中的load(url)方法:

public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
}複製代碼

這個方法分兩步:fromString()load(string)

  • fromString()

public DrawableTypeRequest<String> fromString() {
        return loadGeneric(String.class);
    }複製代碼

z這個方法返回的是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");
        }

        //這句是核心,本質是建立並返回了一個DrawableTypeRequest
        return optionsApplier.apply(
                new DrawableTypeRequest<T>(modelClass, streamModelLoader, 
                        fileDescriptorModelLoader, context,
                        glide, requestTracker, lifecycle, optionsApplier));
    }複製代碼

loadGeneric(Class<T> modelClass)方法中,咱們只須要關注核心便可。它的核心是最後一句,方法調用看着很複雜,其實本質是建立並返回一個DrawableTypeRequest,Drawable類型的請求

  • load(string)

@Override
    public DrawableRequestBuilder<ModelType> load(ModelType model) {
        super.load(model);
        return this;
    }複製代碼

須要注意的是這個方法存在於DrawableTypeRequst的父類DrawableRequestBuilder中,這個方法首先調用DrawableRequestBuilder父類GenericRequestBuilderload()方法,而後返回自身。

再看下DrawableRequestBuilder父類中的load()方法

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
        this.model = model;
        isModelSet = true;
        return this;
    }複製代碼

DrawableRequestBuilder父類GenericRequestBuilder,從名字中咱們也能夠看出來,前者Drawable請求的構建者後者通用的請求構建者,他們是子父關係。

這個load()方法實際上是把咱們傳入的String類型URL存入到內部的model成員變量中,再將數據來源是否已經設置的標誌位isModelSet設置爲true,意味着咱們在調用Glide.with(context).load(url)以後數據來源已經設置成功了。

四、load(url)總結

說到這裏,其實Glide中的load(url)基本已經結束了,可能有的會問:我平時使用Glidehi加一些配置,以下:

Glide.with(context)
    .load(url)
    .placeholder(R.drawable.place_image)
    .error(R.drawable.error_image)
    .into(imageView);複製代碼

其實你們在寫的時候是會有一種感受,這種寫法很想Builder模式。沒錯,這就是一個Builder模式。

通過上面的分析咱們知道,在Glide.with(context).load(url)以後會返回一個DrawableTypeRequest的對象,它的父類DrawableRequestBuilderDrawableRequestBuilder父類GenericRequestBuilder,咱們寫的placeHolder()error()等等相關圖片請求配置的方法定義在GenericRequestBuilder,下面咱們來簡單看看:

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> placeholder(
            int resourceId) {
        this.placeholderId = resourceId;

        return this;
    }
    
    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> error(
            int resourceId) {
        this.errorId = resourceId;

        return this;
    }複製代碼

是否是一會兒就明白了,咱們平時對圖片請求的配置使用的就是Builder模式

五、into(imageView)

簡單的說,Glide中的前兩部是建立了一個Request,這個Request能夠理解爲對圖片加載的配置請求,須要注意的是僅僅是建立了一個請求,而並非去執行。

Glide的最後一步into()方法中,這個請求才會真實的執行。

在DrawableTypeRequest中找下into()方法,發現沒有扎到,那確定是在他的父類DrawableRequestBuilder中,咱們再看看DrawableRequestBuilder中的into()方法:

public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }複製代碼

發現它調用的是父類GenericRequestBuilderinto()方法,那咱們繼續看GenericRequestBuilder中的into()方法:

public Target<TranscodeType> into(ImageView view) {
        //確保在主線程
        Util.assertMainThread();
        //確保view不爲空
        if (view == null) {
            throw new IllegalArgumentException("You must pass in a non null View");
        }
        //對ScaleType進行配置
        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));
    }複製代碼

能夠看到,上面的方法就是into()核心代碼,它定義在GenericRequestBuilder這個通用的請求構建者中。方法的核心是最後一行:into(glide.buildImageViewTarget(view, transcodeClass)),首先經過glide.buildImageViewTarget(view, transcodeClass)建立出一個Target類型的對象,而後把這個target傳入GenericRequestBuilder中的into()方法中。

咱們先來看下Glide中的buildImageViewTarget(view, transcodeClass)方法:

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

這個方法的目的是把咱們傳入的ImageView包裝成一個Target。內部調用了imageViewTargetFactory.buildTarget(imageView, transcodedClass)

繼續跟進看:  

public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
        //圖片來源是GlideDrawable
        if (GlideDrawable.class.isAssignableFrom(clazz)) {
            //建立GlideDrawable對應的target
            return (Target<Z>) new GlideDrawableImageViewTarget(view);
        } else if (Bitmap.class.equals(clazz)) {
            //若是圖片來源是Bitmap,建立Bitmap對應的target
            return (Target<Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            //若是圖片來源是Drawable,建立Drawable對應的target
            return (Target<Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException("Unhandled class: " + clazz
                    + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
    }複製代碼

buildTarget()方法的本質是:經過對圖片來源類型進行判斷,建立並返回與圖片來源對應的imageViewTarget

獲取到相應的target以後,再來看GenericRequestBuilder的into()方法中的return into()方法:

public <Y extends Target<TranscodeType>> Y into(Y target) {
        //確保在主線程
        Util.assertMainThread();
        //確保target不爲空
        if (target == null) {
            throw new IllegalArgumentException("You must pass in a non null Target");
        }
        //確保數據來源已經肯定,即已經調用了load(url)方法
        if (!isModelSet) {
            throw new IllegalArgumentException("You must first set a model (try #load())");
        }

        //獲取當前target已經綁定的Request對象
        Request previous = target.getRequest();

        //若是當前target已經綁定了Request對象,則清空這個Request對象
        if (previous != null) {
            previous.clear();
            //中止綁定到當前target的上一個Request的圖片請求處理
            requestTracker.removeRequest(previous);
            previous.recycle();
        }
        
        //建立Request對象
        Request request = buildRequest(target);
        //與target綁定
        target.setRequest(request);
        lifecycle.addListener(target);
        //執行request
        requestTracker.runRequest(request);

        return target;
    }複製代碼

再次梳理下方法中的邏輯

  • 獲取當前target中的Request對象,若是存在,則清空並終止這個Request對象的執行
  • 建立新的Request對象並與當前target綁定
  • 執行新建立圖片處理請求Request

邏輯仍是比較清晰的,這個有個問題須要說明下:

爲何要終止並清除target以前綁定的請求呢?

在沒有Glide以前,咱們處理ListView中的圖片加載實際上是一件比較麻煩的事情。因爲ListView中item的複用機制,會致使網絡圖片加載的錯位或閃爍。那咱們解決這個問題的方法也很簡單,就是給當前的ImageView設置tag,這個tag能夠是圖片的URL等等。當從網絡中獲取到圖片時判斷這個ImageView中的tag是不是這個圖片的URL,若是加載圖片,不然跳過。

有Glide後,咱們處理ListView或者RecyclerView中的圖片加載就很無腦,根本不須要作任何多餘的操做,直接正常使用就好了。

其實這裏面的原理是Glide給咱們處理了這些判斷,咱們來看下Glide內部如何處理的:

public Request getRequest() {
        //本質仍是getTag
        Object tag = getTag();
        Request request = null;
        if (tag != null) {
            if (tag instanceof Request) {
                request = (Request) tag;
            } else {
                throw new IllegalArgumentException(
                              "You must not call setTag() on a view Glide is targeting");
            }
        }
        return request;
    }
    
    @Override
    public void setRequest(Request request) {
        //本質是setTag
        setTag(request);
    }複製代碼

能夠看到,target.getRequest()target.setRequest(Request request)本質上仍是經過setTaggetTag來作處理,這也印證了咱們上面所說。


繼續回到into()方法中,在建立並綁定Request後,關鍵的就是requestTracker.runRequest(request)來執行咱們建立的請求

public void runRequest(Request request) {
        //將請求加入請求集合
        requests.add(request);
        
        if (!isPaused) {
            若是處於非暫停狀態,開始執行請求
            request.begin();
        } else {
            //若是處於暫停狀態,將請求添加到等待集合
            pendingRequests.add(request);
        }
    }複製代碼

這個方法定義在RequestTracker中,這個類主要負責Request的執行,暫停,取消等等關於圖片請求的操做。咱們着重看request.begin(),這句代碼意味着開始執行圖片請求的處理。Request是個接口,request.begin()實際調用的是Request子類GenericRequestBuilderbegin()方法,咱們跟進看下:

@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 {
            //獲取長寬尺寸,獲取完以後會調用onSizeReady(overrideWidth, overrideHeight)
            target.getSize(this);
        }

        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
            //開始加載圖片,先顯示佔位圖
            target.onLoadStarted(getPlaceholderDrawable());
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
        }
    }複製代碼

begin()方法的邏輯大體以下

  • 獲取圖片的長寬尺寸,若是長寬已經肯定,走onSizeReady(overrideWidth, overrideHeight)流程;若是不肯定,先獲取長寬getSize(this),再走onSizeReady(overrideWidth, overrideHeight)
  • 圖片開始加載,首先顯示佔位圖

能夠看出,主要的邏輯仍是在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));
        }
    }複製代碼

這段代碼看起來很複雜,咱們只須要關心核心代碼:

engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this)

咱們再看看load()方法內部作了哪些處理

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

        //使用LruCache獲取緩存
        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()方法位於Engine類中。load()方法內部會從三個來源獲取圖片的數據,咱們最熟悉的就是LruCache了。如何獲取數據過於複雜,這裏就再也不展開分析,咱們這裏主要是關注圖片數據取得以後的操做。

獲取到圖片數據以後,經過cb.onResourceReady(cached)來處理,再看看這個回調的具體實現

@Override
    public void onResourceReady(Resource<?> resource) {
        if (resource == null) {
            onException(new Exception("Expected to receive a Resource<R> with an object of " 
                    + transcodeClass
                    + " inside, but instead got null."));
            return;
        }

        Object received = resource.get();
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
            releaseResource(resource);
            onException(new Exception("Expected to receive an object of " + transcodeClass
                    + " but instead got " + (received != null ? received.getClass() : "")
                    + "{" + received + "}"
                    + " inside Resource{" + resource + "}."
                    + (received != null ? "" : " "
                        + "To indicate failure return a null Resource object, "
                        + "rather than a Resource object containing null data.")
            ));
            return;
        }

        if (!canSetResource()) {
            releaseResource(resource);
            // We can't set the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } //核心是這一句 onResourceReady(resource, (R) received); }複製代碼

接着看下onResourceReady(resource, (R) received)方法:

private void onResourceReady(Resource<?> resource, R result) {
        // We must call isFirstReadyResource before setting status.
        boolean isFirstResource = isFirstReadyResource();
        status = Status.COMPLETE;
        this.resource = resource;

        if (requestListener == null || 
         !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache, isFirstResource)) {
            GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
            
            //核心,經過調用target的onResourceReady方法加載圖片
            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);
        }
    }複製代碼

咱們能夠看到核心代碼:target.onResourceReady(result, animation),其實在這句代碼的內部最終是經過

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
    public DrawableImageViewTarget(ImageView view) {
        super(view);
    }

    @Override
    protected void setResource(Drawable resource) {
       view.setImageDrawable(resource);
    }
}複製代碼

本質是經過setResource(Drawable resource)來實現的,在這個方法的內部調用了Android內部最經常使用的加載圖片的方法view.setImageDrawable(resource)

六、into()方法總結

到此爲止,into()方法基本已經分析完了,咱們忽略了網絡圖片獲取的過程專一於獲取圖片後的處理。如今來對into()方法作個總結:

  • ImageView包裝成imageViewTarget
  • 清除這個imageViewTarget以前綁定的請求綁定新的請求
  • 執行新的請求
  • 獲取圖片數據以後,成功則會調用ImageViewTarget中的onResourceReady()方法,不然則會調用ImageViewTarget中的onLoadFailed()兩者的本質都是經過調用Android中的imageView.setImageDrawable(drawable)來實現對ImageView圖片加載


流程圖



以上所有內容來源於:Glide源碼分析

相關文章
相關標籤/搜索