詳談高大上的圖片加載框架Glide -源碼篇

在上篇Glide文章中系吩,我們介紹了Glide圖片加載框架的使用,通過之前的學習州胳,我們可能已經(jīng)能熟練的將Glide圖片加載框架運用到我們的項目中舵变,但是如果有人問你它是如何加載酣溃,工作原理是怎樣的?為什么自定義GlideModule只需要在Manifest文件中加入meta-data即可纪隙?等等很多加載流程以及使用的注意事項赊豌。當然要想搞明白這些問題,就需要我們對Glide源碼有個大致的認識绵咱,去剖析源碼深處的奧秘碘饼。
接下來就讓我們一起去進入Glide的源碼世界,本篇文章分析的是Glide 3.7.0版本。特別提醒艾恼,閱讀本片文章之前要對Glide的用法要先有一個了解住涉,可以先閱讀上篇文章,詳談高大上的圖片加載框架Glide -應用篇钠绍。

此篇文章是自己學習的一個記錄舆声,若對閱讀文章的你有一定幫助,很是高興柳爽,當然文章如有不足或者錯誤的地方媳握,歡迎指正,避免我給其他讀者錯誤引導磷脯。

如果你閱讀過上篇文章蛾找,或者你使用過Glide,就知道Glide加載圖片的最簡單方式就是

Glide.with(context).load(url). placeholder(R.drawable.placeholder).into(imageView)赵誓。

那么這篇文章就以這句簡單的代碼為主線打毛,逐步深入Glide的源碼。

Glide.with(context)

   //獲取RequestManager對象俩功,該類實現(xiàn)了LifeCycleListener接口幻枉,綁定Activity/Fragment生命周期,對請求進行暫停绑雄,恢復展辞,清除操作
    public static RequestManager with(Context context) {
    //得到RequestManagerRetriever實例,該類注意將RequestManager和自定義Fragment(如RequestManagerFragment万牺,SupportRequestManagerFragment)綁定,從而實現(xiàn)在生命周期管理回調(diào)
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }

Glide有四個靜態(tài)的重載方法with()洽腺,其內(nèi)部都通過RequestManagerRetriever相應的get重載方法獲取一個RequestManager對象脚粟。RequestManagerRetriever提供各種重載方法的好處就是可以將Glide的加載請求與Activity/Fragment的生命周期綁定而自動執(zhí)行請求,暫停操作蘸朋。

接下來我們拿Activity參數(shù)分析Glide請求如何和綁定生命周期自動請求核无,暫停,以及銷毀藕坯。


    public RequestManager get(Activity activity) {
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return get(activity.getApplicationContext());
        } else {
        
           //判斷activity是否已經(jīng)是銷毀狀態(tài)
            assertNotDestroyed(activity);
            //獲取FragmentManager 對象
            android.app.FragmentManager fm = activity.getFragmentManager();
            //創(chuàng)建Fragment团南,RequestManager并將其綁定
            return fragmentGet(activity, fm);
        }
    }

assertNotDestroyed主要斷言Activity是否已經(jīng)Destroyed。若是沒有銷毀炼彪,或者Activity的FragmentManager 吐根,然后通過fragmentGet返回RequestManager。

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
      //*獲取RequestManagerFragment辐马,主要利用Frament進行請求的生命周期管理
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        //requestManager 為空拷橘,即首次加載初始化requestManager ,并調(diào)用setRequestManager設(shè)置到RequestManagerFragment 
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

 //獲取Fragment對象
    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;
    }

最終通過getRequestManagerFragment()方法獲取一個RequestManagerFragment 對象。

public class RequestManagerFragment extends Fragment {
    private final ActivityFragmentLifecycle lifecycle;
    //省略部分代碼...
    @Override
    public void onStart() {
        super.onStart();
        //關(guān)聯(lián)lifecycle相應onStart方法
        lifecycle.onStart();
    }

    @Override
    public void onStop() {
        super.onStop();
         //關(guān)聯(lián)lifecycle相應onStop方法
        lifecycle.onStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
         //關(guān)聯(lián)lifecycle相應onDestroy方法
        lifecycle.onDestroy();
    }
}

此時我們看到RequestManagerFragment 繼承了Fragment.并且在其生命周期onStart(),onStop(),onDestory()冗疮,調(diào)用了ActivityFragmentLifecycle 相應的方法萄唇,ActivityFragmentLifecycle實現(xiàn)了Lifecycle 接口,在其中通過addListener(LifecycleListener listener)回調(diào)相應(LifecycleListener的 onStart(),onStop(),onDestory())周期方法术幔。LifecycleListener是監(jiān)聽生命周期時間接口另萤。
再次回到fragmentGet方法里下面一句代碼


//創(chuàng)建RequestManager傳入Lifecycle實現(xiàn)類,如ActivityFragmentLifecycle
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());

對于RequestManager類诅挑,該類實現(xiàn)了LifecycleListener四敞,如下代碼

/**
 * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to
 * intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage
 * built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity.
 */
public class RequestManager implements LifecycleListener {
  //An interface for listening to Activity/Fragment lifecycle events.
    private final Lifecycle lifecycle;
 public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }

    RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
            RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
        this.context = context.getApplicationContext();
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        //A class for tracking, canceling, and restarting in progress, completed, and failed requests.
        this.requestTracker = requestTracker;
        //通過Glide的靜態(tài)方法獲取Glide實例。單例模式
        this.glide = Glide.get(context);
        this.optionsApplier = new OptionsApplier();

//通過工廠類ConnectivityMonitorFactory的build方法獲取ConnectivityMonitor (一個用于監(jiān)控網(wǎng)絡(luò)連接事件的接口)
        ConnectivityMonitor connectivityMonitor = factory.build(context,
                new RequestManagerConnectivityListener(requestTracker));

        // If we're the application level request manager, we may be created on a background thread. In that case we
        // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
        // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
        if (Util.isOnBackgroundThread()) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    lifecycle.addListener(RequestManager.this);
                }
            });
        } else {
        //設(shè)置監(jiān)聽
            lifecycle.addListener(this);
        }
        lifecycle.addListener(connectivityMonitor);
    }
    /**
     * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
     * permission is present) and restarts failed or paused requests.
     */
    @Override
    public void onStart() {
        // onStart might not be called because this object may be created after the fragment/activity's onStart method.
        resumeRequests();
    }

    /**
     * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
     * permission is present) and pauses in progress loads.
     */
    @Override
    public void onStop() {
        pauseRequests();
    }

    /**
     * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed
     * requests.
     */
    @Override
    public void onDestroy() {
        requestTracker.clearRequests();
    }
}

它將剛創(chuàng)建的fragment的lifeCycle傳入揍障,并將RequestManager這個listener添加到lifeCycle中目养,從而實現(xiàn)綁定。在RequestManager的構(gòu)造方法里看到了requestTracker毒嫡,該對象就是跟蹤請求取消癌蚁,重啟,完成兜畸,失敗努释。RequestManagerFragment 主要是用來連接生命周期方法,RequestManager用來實現(xiàn)生命周期中請求方法咬摇,而RequestManagerRetriever綁定了RequestManager伐蒂。

GlideModule實現(xiàn)

在構(gòu)造方法中還初始化了通過Glide.get(context);初始化了Glide對象

    /**
     * Get the singleton.
     *
     * @return the singleton
     */
    public static Glide get(Context context) {
    
        if (glide == null) {
        //同步Glide
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();
                    //解析清單文件配置的自定義GlideModule的metadata標簽,返回一個GlideModule集合
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();

                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    //循環(huán)集合肛鹏,執(zhí)行GlideModule 實現(xiàn)類中的方法
                    for (GlideModule module : modules) {
                        module.applyOptions(applicationContext, builder);
                    }
                    glide = builder.createGlide();
                    for (GlideModule module : modules) {
                    //注冊組件
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }

通過get方法單例方式獲取實例逸邦,并在初始化時實現(xiàn)了GlideModule配置功能。具體怎么實現(xiàn)的呢在扰?接下來具體分析一下缕减,在初始化時new 了一個ManifestParser對象并且調(diào)用了parse()方法返回一個GlideModule類型的List.

//解析metadata具體實現(xiàn)
    public List<GlideModule> parse() {
        List<GlideModule> modules = new ArrayList<GlideModule>();
        try {
        //通過PackageManager獲取metadata所有信息
            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
                    context.getPackageName(), PackageManager.GET_META_DATA);
             //清單文件含有metadata
            if (appInfo.metaData != null) {
            //通過key遍歷metadata(對于GlideModule,key就是GlideModule的實現(xiàn)類的全路徑類名)
                for (String key : appInfo.metaData.keySet()) {
                //過濾key對應的value等于GLIDE_MODULE_VALUE(字符串GlideModule)
                    if (GLIDE_MODULE_VALUE.equals(appInfo.metaData.get(key))) {
                        //符合條件加入集合中
                        modules.add(parseModule(key));
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException("Unable to find metadata to parse GlideModules", e);
        }

        return modules;
    }

在parse()方法中通過getApplicationInfo方法獲取metaData信息芒珠,若有metaData數(shù)據(jù)(appInfo.metaData != null)桥狡,如果metaData的值為GlideModule則調(diào)用parseModule(key),方法返回GlideModule并add到返回的List中皱卓。

查看parseModule(String className)方法

//通過反射獲取GlideModule實例
    private static GlideModule parseModule(String className) {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Unable to find GlideModule implementation", e);
        }

        Object module;
        try {
            module = clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
        }

        if (!(module instanceof GlideModule)) {
            throw new RuntimeException("Expected instanceof GlideModule, but found: " + module);
        }
        return (GlideModule) module;
    }

到此我們看到通過反射的方式獲取我們在清單文件中聲明的自定義的GlideModule對象裹芝。在獲取到
GlideModule集合之后,遍歷了集合并調(diào)用相應的applyOptions和registerComponents方法娜汁,而Glide對象的生成是通過GlideBuilder的createGlide方法創(chuàng)建嫂易。

 Glide createGlide() {
        if (sourceService == null) {
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            //初始化線程池
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }

        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
         //設(shè)置Bitmap池
        if (bitmapPool == null) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }

        if (memoryCache == null) {
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }

        if (diskCacheFactory == null) {
        //內(nèi)部磁盤緩存
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }

        if (engine == null) {
        //初始化引擎類
            engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
        }

        if (decodeFormat == null) {
            decodeFormat = DecodeFormat.DEFAULT;
        }

        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }

看到這都是做的一些初始化操作,并將參數(shù)傳遞到Glide構(gòu)造方法存炮。對于Glide構(gòu)造方法做的都是一些默認的初始化操作炬搭,可以自己去查看源碼蜈漓,此處不再貼出。

通過上面的分析宫盔,你就會理解融虽,為什么之前提到配置信息只需要實現(xiàn)GlideModule接口,重寫其中的方法灼芭,并再清單文件配置metaData有额,并且metaData的key是自定義GlideModule的全路徑名,value值必須是GlideModule.會明白當我們不想讓自定義的GlideModule生效時只需要刪除相應的GlideModule彼绷。當使用了混淆時為什么要配置...

-keep public class * implements com.bumptech.glide.module.GlideModule

requestManager.load##

對于load方法也是可以接收String巍佑,Url,Integer等類型的重載方法寄悯,在這里萤衰,我們拿String類型參數(shù)分析。

    public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }
    public DrawableTypeRequest<String> fromString() {
        return loadGeneric(String.class);
    }
   private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
        // 省略一段代碼

        return optionsApplier.apply(
        // 創(chuàng)建DrawableTypeRequest猜旬,它是GenericRequestBuilder的子類
                new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                        glide, requestTracker, lifecycle, optionsApplier));
    }
    @Override
    public DrawableRequestBuilder<ModelType> load(ModelType model) {
    //調(diào)用弗雷loadd方法
        super.load(model);
        return this;
    }

返回的是DrawableTypeRequest對象脆栋,DrawableTypeRequest繼承關(guān)系如下


這里寫圖片描述

,而對于DrawableRequestBuilder類使用的是一個創(chuàng)建者模式洒擦,對于常用函數(shù)placeholder()椿争,error(),transform等設(shè)置都是在此設(shè)置熟嫩,

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) {
        super.placeholder(drawable);
        return this;
    }

我們看到最終又調(diào)用了父類方法

    /**
     * Sets an Android resource id for a {@link android.graphics.drawable.Drawable} resourceto display while a resource
     * is loading.
     *
     * @param resourceId The id of the resource to use as a placeholder
     * @return This request builder.
     */
    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> placeholder(
            int resourceId) {
        this.placeholderId = resourceId;

        return this;
    }

通過查看父類(GenericRequestBuilder)源碼你會發(fā)現(xiàn)我們每次調(diào)用placeholder()秦踪,error()的等這些方法,其實都是給該類中的變量賦值掸茅。

經(jīng)過一系列操作后椅邓,最終調(diào)用into(imageView)方法來完成圖片的最終加載

創(chuàng)建請求

    /**
     * Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into the view, and frees
     * any resources Glide may have previously loaded into the view so they may be reused.
     *
     * @see Glide#clear(android.view.View)
     *
     * @param view The view to cancel previous loads for and load the new resource into.
     * @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
     */
    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));
    }

由上面看到最終調(diào)用的into方法是

    /**
     * Set the target the resource will be loaded into.
     *
     * @see Glide#clear(com.bumptech.glide.request.target.Target)
     *
     * @param target The target to load the resource into.
     * @return The given target.
     */
    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 對象
        Request previous = target.getRequest();
//requestTracker是請求跟蹤類對象,主要管理請求的發(fā)起昧狮,暫停希坚,清除
        if (previous != null) {
            previous.clear();
            requestTracker.removeRequest(previous);
            previous.recycle();
        }

       //創(chuàng)建請求對象
        Request request = buildRequest(target);
        target.setRequest(request);
        //將target加入lifecycle
        lifecycle.addListener(target);
        //執(zhí)行請求
        requestTracker.runRequest(request);

        return target;
    }

上面都執(zhí)行都調(diào)用了 Util.assertMainThread();判斷只能在主線程中執(zhí)行。(更新View當然需要在主線程)陵且,在Glide中Target我們可以理解成View,只是Glide對我們的View做了一層封裝。
之后通過buildRequest創(chuàng)建請求對象个束。

//創(chuàng)建請求對象
   private Request buildRequest(Target<TranscodeType> target) {
        if (priority == null) {
        //默認加載優(yōu)先級 NORMAL
            priority = Priority.NORMAL;
        }
        //創(chuàng)建Request 
        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);
        }
    }

最后調(diào)用obtainRequest方法

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

最終通過GenericRequest.obtain方法創(chuàng)建了

    public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(...) {
        @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>();
        }
        //利用設(shè)置的參數(shù)初始化Request對象
        request.init(...);
        //返回Request對象
        return request;
    }

至此請求對象創(chuàng)建成功慕购,在通過buildRequest創(chuàng)建請求成功后,使用了target.setRequest(request);將請求設(shè)置到target,并通過addListener將target加入到lifecycle茬底。上面執(zhí)行了那么多都只是請求創(chuàng)建沪悲,請求的執(zhí)行時通過requestTracker.runRequest(request);開始的。

發(fā)送請求

    /**
     * Starts tracking the given request.
     */
    public void runRequest(Request request) {
    //添加request對象到集合中
        requests.add(request);
        if (!isPaused) {
        //如果當前狀態(tài)是非暫停的阱表,調(diào)用begin方法發(fā)送請求
            request.begin();
        } else {
        //將請求加入到掛起的請求集合
            pendingRequests.add(request);
        }
    }

在上面幾句代碼殿如,我們看到贡珊,每次提交請求都將請求加入了一個set中,用它來管理請求涉馁,然后通過request的實現(xiàn)類GenericRequest查看begin方法執(zhí)行的內(nèi)容

    /**
     * {@inheritDoc}
     */
    @Override
    public void begin() {
        startTime = LogTime.getLogTime();
        if (model == null) {
        //加載錯誤占位圖設(shè)置
            onException(null);
            return;
        }

        status = Status.WAITING_FOR_SIZE;
        //驗證寬高是否合法
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            //發(fā)送請求
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            target.getSize(this);
        }

        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
        //加載前默認占位圖設(shè)置回調(diào)
            target.onLoadStarted(getPlaceholderDrawable());
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
        }
    }
    //獲取設(shè)置加載開始時占位圖片的Drawable 對象
    private Drawable getPlaceholderDrawable() {
        if (placeholderDrawable == null && placeholderResourceId > 0) {
            placeholderDrawable = context.getResources().getDrawable(placeholderResourceId);
        }
        return placeholderDrawable;
    }

上面有一句!isComplete() && !isFailed() && canNotifyStatusChanged()判斷门岔,如果都為真會回調(diào)target.onLoadStarted(getPlaceholderDrawable());我們可以看到Target的實現(xiàn)類ImageViewTarget中onLoadStarted的回調(diào)執(zhí)行語句

//給ImageView設(shè)置Drawable 
    @Override
    public void onLoadStarted(Drawable placeholder) {
        view.setImageDrawable(placeholder);
    }

現(xiàn)在你是不是有一種柳暗花明又一村的感覺,終于明白為什么設(shè)置placeHolder后烤送,會在加載前有一個占位圖寒随,當然設(shè)置加載錯誤圖片占位圖的原理也是一樣的。只不過回調(diào)執(zhí)行時機不同帮坚。

    /**
     * A callback method that should never be invoked directly.
     */
    @Override
    public void onSizeReady(int width, int height) {
//省略部分代碼
        status = Status.RUNNING;//將請求狀態(tài)更新為運行狀態(tài)
//省略部分代碼

// 進入Engine的入口妻往,請求執(zhí)行的核心方法
        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類封裝了數(shù)據(jù)獲取的重要入口方法,向request層提供這些API试和,比如load(), release(), clearDiskCache()等方法

    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();
        //創(chuàng)建Enginekey
        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) {
        // 獲取數(shù)據(jù)成功讯泣,會回調(diào)target的onResourceReady()
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }
// 嘗試從活動Resources 中獲取,它表示的是當前正在使用的Resources阅悍,與內(nèi)存緩存不同之處是clear緩存時不會clear它好渠。
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
           //獲取成功回調(diào)
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }

        EngineJob current = jobs.get(key);
        //判斷jobs中是否已經(jīng)存在任務,如果存在說明任務之前已經(jīng)提交了
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }

//緩存沒有獲取到溉箕,創(chuàng)建EngineJob 對象
        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 是任務執(zhí)行階段的入口
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        // 開始提交job
        engineJob.start(runnable);

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }

我們看到先根據(jù)調(diào)用loadFromCache從內(nèi)存加載晦墙,若返回值為空再次從活動的資源中加載,若再次為空查看jobs是否提交過任務肴茄,若沒有提交則創(chuàng)建EngineRunnable晌畅,并將任務提交到engineJob中。我們先看下EngineJob中的start方法

   //提交任務寡痰,將任務加入到線程池
    public void start(EngineRunnable engineRunnable) {
        this.engineRunnable = engineRunnable;
        //提交任務到diskCacheService線程池
        future = diskCacheService.submit(engineRunnable);
    }

接下來看線程類EngineRunnable的run方法抗楔,它是任務執(zhí)行的入口

//任務運行入口
 @Override
    public void run() {
        if (isCancelled) {
            return;
        }

        Exception exception = null;
        Resource<?> resource = null;
        try {
        //數(shù)據(jù)的獲取,編解碼
            resource = decode();
        } catch (Exception e) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Exception decoding", e);
            }
            exception = e;
        }
//如果當前狀態(tài)是取消拦坠,則回收各種資源防止內(nèi)存泄露
        if (isCancelled) {
            if (resource != null) {
                resource.recycle();
            }
            return;
        }

        if (resource == null) {
        //加載失敗回調(diào)
            onLoadFailed(exception);
        } else {
        //加載成功回調(diào)
            onLoadComplete(resource);
        }
    }
    private Resource<?> decode() throws Exception {
        if (isDecodingFromCache()) {
        //// 從DiskLruCache中獲取數(shù)據(jù)并解碼
            return decodeFromCache();
        } else {
        // 從其他途徑獲取數(shù)據(jù)并解碼连躏,如網(wǎng)絡(luò),本地File贞滨,數(shù)據(jù)流等
            return decodeFromSource();
        }
    }

DiskLruCache獲取數(shù)據(jù)

    private Resource<?> decodeFromCache() throws Exception {
        Resource<?> result = null;
        try {
            result = decodeJob.decodeResultFromCache();
        } catch (Exception e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Exception decoding result from cache: " + e);
            }
        }

        if (result == null) {
            result = decodeJob.decodeSourceFromCache();
        }
        return result;
    }

之后調(diào)用decodeJob類中的decodeResultFromCache

 public Resource<Z> decodeResultFromCache() throws Exception {
        if (!diskCacheStrategy.cacheResult()) {
            return null;
        }

        long startTime = LogTime.getLogTime();
        //從DiskCache中獲取資源
        Resource<T> transformed = loadFromCache(resultKey);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Decoded transformed from cache", startTime);
        }
        startTime = LogTime.getLogTime();
        Resource<Z> result = transcode(transformed);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transcoded transformed from cache", startTime);
        }
        return result;
    }
    //從DiskCache中獲取資源
    private Resource<T> loadFromCache(Key key) throws IOException {
    //根據(jù)key從DiskCache獲取文件
        File cacheFile = diskCacheProvider.getDiskCache().get(key);
        if (cacheFile == null) {
            return null;
        }

        Resource<T> result = null;
        try {
            result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
        } finally {
            if (result == null) {
                diskCacheProvider.getDiskCache().delete(key);
            }
        }
        return result;
    }

接下來我們分析decodeFromSource方法

// 調(diào)用decodeJob來完成數(shù)據(jù)獲取和編解碼
    private Resource<?> decodeFromSource() throws Exception {
        return decodeJob.decodeFromSource();
    }
    public Resource<Z> decodeFromSource() throws Exception {
    // 獲取數(shù)據(jù)入热,解碼
        Resource<T> decoded = decodeSource();
        //編碼并保存
        return transformEncodeAndTranscode(decoded);
    }
 // 獲取數(shù)據(jù),解碼
    private Resource<T> decodeSource() throws Exception {
        Resource<T> decoded = null;
        try {
            long startTime = LogTime.getLogTime();
            //數(shù)據(jù)拉取
            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;
    }

在數(shù)據(jù)獲取時先調(diào)用DataFetcher的loadData()拉取數(shù)據(jù)晓铆,對于DataFetcher的實現(xiàn)類有好幾個勺良,我們拿從url拉取數(shù)據(jù)為例,也就是HttpUrlFetcher類

   @Override
    public InputStream loadData(Priority priority) throws Exception {
        return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
    }
//返回InputStream 對象
    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
            throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
            throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
            // Comparing the URLs using .equals performs additional network I/O and is generally broken.
            // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
            try {
                if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                    throw new IOException("In re-direct loop");
                }
            } catch (URISyntaxException e) {
                // Do nothing, this is best effort.
            }
        }
        // 靜態(tài)工廠模式創(chuàng)建HttpURLConnection對象
        urlConnection = connectionFactory.build(url);
        for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
          urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        //設(shè)置請求參數(shù)
        //設(shè)置連接超時時間2500ms
        urlConnection.setConnectTimeout(2500);
        //設(shè)置讀取超時時間2500ms
        urlConnection.setReadTimeout(2500);
        //不使用http緩存
        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());
        }
    }

看到這終于看到了網(wǎng)絡(luò)加載請求骄噪,我們也可以自定義DataFetcher尚困,從而使用其他網(wǎng)絡(luò)庫,如OkHttp链蕊,Volley.
最后我們再看下transformEncodeAndTranscode方法

 private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
        long startTime = LogTime.getLogTime();
        // 根據(jù)ImageView的scaleType等參數(shù)計算真正被ImageView使用的圖片寬高事甜,并保存真正寬高的圖片谬泌。
        Resource<T> transformed = transform(decoded);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transformed resource from source", startTime);
        }
  // 寫入到DiskLruCache中,下次就可以直接從DiskLruCache獲取使用
        writeTransformedToCache(transformed);

        startTime = LogTime.getLogTime();
          // 轉(zhuǎn)碼逻谦,將源圖片轉(zhuǎn)碼為ImageView所需的圖片格式
        Resource<Z> result = transcode(transformed);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transcoded transformed from source", startTime);
        }
        return result;
    }

至此掌实,圖片加載流程已經(jīng)介紹完畢,當然還有很多的地方?jīng)]有提到跨跨,相信如果你在閱讀本文的同時潮峦,自己跟蹤源碼會輕松很多,如果自己不跟著源碼走的話勇婴,可能這篇文章看幾遍對Glide原理理解的也是云里霧里忱嘹,或者說當時看的懂,但是很快就不記得耕渴。所以切記自己要跟著源碼過一遍拘悦。

本片文章實在是長,能讀完本文章也是需要一定毅力的...若文章有不足或者錯誤的地方橱脸,歡迎指正础米,以防止給其他讀者錯誤引導。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末添诉,一起剝皮案震驚了整個濱河市屁桑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栏赴,老刑警劉巖蘑斧,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異须眷,居然都是意外死亡竖瘾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門花颗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捕传,“玉大人,你說我怎么就攤上這事扩劝∮孤郏” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵棒呛,是天一觀的道長葡公。 經(jīng)常有香客問我,道長条霜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任涵亏,我火速辦了婚禮宰睡,結(jié)果婚禮上蒲凶,老公的妹妹穿的比我還像新娘。我一直安慰自己拆内,他們只是感情好旋圆,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著麸恍,像睡著了一般灵巧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抹沪,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天刻肄,我揣著相機與錄音,去河邊找鬼融欧。 笑死敏弃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的噪馏。 我是一名探鬼主播麦到,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欠肾!你這毒婦竟也來了瓶颠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤刺桃,失蹤者是張志新(化名)和其女友劉穎粹淋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虏肾,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡廓啊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了封豪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谴轮。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吹埠,靈堂內(nèi)的尸體忽然破棺而出第步,到底是詐尸還是另有隱情,我是刑警寧澤缘琅,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布粘都,位于F島的核電站,受9級特大地震影響刷袍,放射性物質(zhì)發(fā)生泄漏翩隧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一呻纹、第九天 我趴在偏房一處隱蔽的房頂上張望堆生。 院中可真熱鬧专缠,春花似錦、人聲如沸淑仆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔗怠。三九已至墩弯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寞射,已是汗流浹背渔工。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怠惶,地道東北人涨缚。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像策治,于是被迫代替她去往敵國和親脓魏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在Android設(shè)備上通惫,加載網(wǎng)絡(luò)圖片一直是一個頭疼的問題茂翔,因為Android設(shè)備種類繁多(當然最主要的是配置),處...
    Code4Android閱讀 17,677評論 5 96
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,160評論 25 707
  • 斷舍離 我生活狀態(tài)的轉(zhuǎn)變履腋,都是歸功于這一本書珊燎,從這里理清了自己的追求,同時還有整理干凈了自己的生活瑣事遵湖。斷舍離也是...
    酷酷的我閱讀 179評論 0 0
  • 如果讓我說出一部最喜歡的周星馳的電影延旧,我恐怕沒辦法排序谋国。《喜劇之王》迁沫,《少林足球》芦瘾,《逃學威龍系列》等我都很喜歡。...
    飛魚man閱讀 780評論 0 4
  • 文/thx2956 你若一幅畫面浮現(xiàn) 穿越我的眼簾 輕撫容顏 指尖詩意飄散 你若一首歌繞梁 掛我嘴邊輕唱 音符歡跳...
    神呼吸閱讀 559評論 10 10