深入理解Glide

Glide是一個快速高效的Android圖片加載庫,注重于平滑的滾動晃虫。Glide提供了易用的API皆撩,高性能、可擴展的圖片解碼管道(decode pipeline)哲银,以及自動的資源池技術(shù)扛吞。

目前,Glide的最新版本為4.2.0荆责,本文是基于4.1.1這個版本來分析的滥比,同屬4.x,變化不大做院。

本文首發(fā)博客

基本用法

多數(shù)情況下盲泛,使用Glide加載圖片非常簡單,一行代碼足矣:

Glide.with(fragment)
    .load(myUrl)
    .into(imageView);

取消加載同樣很簡單:

Glide.with(fragment).clear(imageView);

  • asBitmap() //指定加載靜態(tài)圖片山憨,如果是gif則加載第一幀查乒。
  • asGif() //如果是非gif弥喉,則加載失敗郁竟。
  • asXxx() // 較3.x新增了幾個as方法。

注解生成流式API(與3.x版本最大區(qū)別)

Glide v4 使用 注解處理器 (Annotation Processor) 來生成出一個 API由境,在 Application 模塊中可使用該流式 API 一次性調(diào)用到 RequestBuilder棚亩, RequestOptions 和集成庫中所有的選項蓖议。

Generated API 模式的設計出于以下兩個目的

  • 集成庫可以為 Generated API 擴展自定義選項。
  • 在 Application 模塊中可將常用的選項組打包成一個選項在 Generated API 中使用

雖然以上所說的工作均可以通過手動創(chuàng)建 RequestOptions 子類的方式來完成讥蟆,但想將它用好更具有挑戰(zhàn)勒虾,并且降低了 API 使用的流暢性。

使用 Generated API

Generated API 默認名為 GlideApp 瘸彤,與 Application 模塊中 AppGlideModule的子類包名相同修然。在 Application 模塊中將 Glide.with() 替換為 GlideApp.with(),即可使用該 API 去完成加載工作:

GlideApp.with(fragment)
   .load(myUrl)
   .placeholder(R.drawable.placeholder)
   .fitCenter()
   .into(imageView);

與 Glide.with() 不同质况,諸如 fitCenter() 和 placeholder() 等選項在 Builder 中直接可用愕宋,并不需要再傳入單獨的 RequestOptions 對象。

當然结榄,Glide也支持kotlin中贝,更多用法請參考官網(wǎng)。
Glide中文文檔臼朗、英文文檔

主要執(zhí)行流程

本節(jié)主要從源碼角度分析Glide的主體流程邻寿,相信閱讀完本節(jié)內(nèi)容,你對Glide會有更清晰的認識视哑。

簡易流程圖

glide-flow-diagram

你可能現(xiàn)在看不太懂绣否,沒關(guān)系。下面會從源碼角度分析Glide整個執(zhí)行過程黎炉,完了之后枝秤,我們在回過來看也許就明白了。

由于Glide源碼較復雜慷嗜,閱讀前最好明確目標淀弹,認準一個功能點,然后分析這個功能點如何實現(xiàn)即可庆械,只追求主體實現(xiàn)邏輯薇溃,莫糾纏細節(jié),點到為止缭乘。你說沐序,我就想琢磨細節(jié)實現(xiàn)咋辦?待下個回合將你這個細節(jié)作為目標帶入分析堕绩,如此循環(huán)策幼,各個擊破。

以上奴紧,是我閱讀源碼的一些建議特姐,希望對你有幫助。

下面黍氮,我們就以圖作路唐含,分析下面這句代碼浅浮。

Glide.with(fragment).load(myUrl).into(imageView);

目標很明確。Glide是如何將這張圖片加載并顯示到組件上的捷枯? 從問題切入滚秩,到代碼里找答案。

把圖片加載并顯示淮捆,這個過程郁油,我理解就三步:

  1. 創(chuàng)建request
  2. 執(zhí)行加載
  3. 回調(diào)刷新UI

創(chuàng)建request

獲取RequestManager(初次會實例化Glide)

  • 從Glide.with()方法開始
  public static RequestManager with(Activity activity) {
    return getRetriever(activity).get(activity);
  }
  
  public static RequestManager with(FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }
  
  public static RequestManager with(Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
  
  public static RequestManager with(android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
  
   public static RequestManager with(Context context) {
    return getRetriever(context).get(context);
  }
  
  //4.x新增
  public static RequestManager with(View view) {
    return getRetriever(view.getContext()).get(view);
  }

可以看出,with方法重載種類多攀痊,值得說一點是已艰,4.x新增了View參數(shù)的重載,這樣便于在view類中使用蚕苇。with方法比較簡單河哑,重載也是為了方便調(diào)用萎庭。

我們要知道RequestManager對象是怎么創(chuàng)建的?就先來看看getRetriever()方法。

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a  Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }
  
   /**
   * Get the singleton.
   *
   * @return the singleton
   */
  public static Glide get(Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }

    return glide;
  }
  

以上代碼可以看出法梯,RequestManagerRetriever對象通過Glide實例獲取桶蛔,而Glide實例是通過單利模式創(chuàng)建的皇筛,這里單利也是經(jīng)典的“雙重校驗”模式避凝。有關(guān)Glide實例化的細節(jié),我們后面用到再講恩沽。

那問題簡單了誊稚,RequestManager對象就是通過RequestManagerRetriever的get方法創(chuàng)建并返回的。

//get方法也像with一樣罗心,有多個重載里伯,這里只貼出一個代表性的。
public RequestManager get(Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    //num=0 這里如果是非主線程渤闷,直接返回applicationManager
    } 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());
      }
    }
    //傳入的Context為ApplicationContext
    return getApplicationManager(context);
  }
  
  //get(Activity)/get(Fragment)邏輯差不多疾瓮,這里分析以下這種類型。
  public RequestManager get(FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, null /*parentHint*/);
    }
  }
  
  private RequestManager supportFragmentGet(Context context, FragmentManager fm,
      Fragment parentHint) {
      //num=1 獲取managerFragment
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      //num=2 創(chuàng)建requestManager 飒箭,并傳入了Lifecycle
      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
     //num=3 緩存requestManager,保證一個Activity對應一個requestManager
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
  
  //num=4 創(chuàng)建并添加一個SupportRequestManagerFragment
  SupportRequestManagerFragment getSupportRequestManagerFragment(
      final FragmentManager fm, Fragment parentHint) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingSupportRequestManagerFragments.put(fm, current);
        //num=5 這里添加一個隱藏的Fragment
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

以上為RequestManagerRetriever類狼电,我只貼了部分重要的代碼。

可以看出get方法重載參數(shù)雖然很多弦蹂,但最終就返回兩種類型的requestManager肩碟。一種是ApplicationManager,它自動和應用的生命周期同步,應用退出凸椿,Glide也就停止加載削祈;另外一種則是帶有Fragment生命周期的requestManager。對應上述代碼中 num=1-5注釋削饵,可以看出岩瘦,Glide添加一個隱藏的Fragment,獲取對應的生命周期回調(diào)事件窿撬,這樣就可在Activity銷毀時停止加載圖片了启昧。這種方式比較巧妙,不僅是Glide劈伴,RxPermission項目也是這樣使用的密末。

到這里Glide.with()方法就分析完了。它主要完成了Glide實例化跛璧,并返回requestManager對象严里。

通過RequestBuilder創(chuàng)建Request

  • 跟進load(url)方法
//這里默認調(diào)用的asDrawable,表示下載后要轉(zhuǎn)換的類型追城。
//當然也可以設置其它類型刹碾,比如asBitmap、asFile座柱,都類似迷帜,這里就不展開分析了。
  public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }
  
  //最終后走到這里色洞。
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

load方法比較簡單戏锹,根據(jù)傳入的model類型對應有多個重載,但最終也只是將其緩存到model變量火诸。這一節(jié)锦针,我們是分析request的創(chuàng)建,現(xiàn)在到了RequestBuilder置蜀,看名字就知道是創(chuàng)建request的奈搜,哪它在哪里創(chuàng)建的呢?我們接著看into方法盯荤。

public Target<TranscodeType> into(ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      if (requestOptions.isLocked()) {
        requestOptions = requestOptions.clone();
      }
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions.optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions.optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions.optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions.optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    //以上邏輯是轉(zhuǎn)換相關(guān)的媚污,不看。只關(guān)注這句代碼廷雅。
    return into(context.buildImageViewTarget(view, transcodeClass));
  }
  
  //創(chuàng)建一個ImageViewTarget耗美,用于后續(xù)更新UI
  public <X> Target<X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
  
  public class ImageViewTargetFactory {

  //根據(jù)類型返回target。因為load方法默認使用的是asDrawable航缀,這里默認返回的是DrawableImageiewTarget商架。
  @SuppressWarnings("unchecked")
  public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
    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)");
    }
  }
}

//target創(chuàng)建好了,就接著看into(Y target)方法

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    requestOptions.lock();
    //這里創(chuàng)建的request
    Request request = buildRequest(target);
    Request previous = target.getRequest();
    
    if (request.isEquivalentTo(previous)
      && (Preconditions.checkNotNull(previous).isComplete()
         || Preconditions.checkNotNull(previous).isRunning())) {
      request.recycle();
      
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    //這里執(zhí)行request
    requestManager.track(target, request);

    return target;
  }
  
  //接著看buildRequest(target)
  
  private Request buildRequest(Target<TranscodeType> target) {
    return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(),
        requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight());
  }

  private Request buildRequestRecursive(Target<TranscodeType> target,
      @Nullable ThumbnailRequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority, int overrideWidth, int overrideHeight) {
    
      // 省略一大堆其它邏輯代碼(縮放相關(guān))
      // Base case: no thumbnail.
      return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority,
          overrideWidth, overrideHeight);
    
  }

  private Request obtainRequest(Target<TranscodeType> target,
      RequestOptions requestOptions, RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority,
      int overrideWidth, int overrideHeight) {
    requestOptions.lock();
    //可以看到這里通過SingleRequest.obtain創(chuàng)建的Request芥玉,內(nèi)部就是new一個SingleRequest對象并賦值相關(guān)屬性蛇摸,就不貼代碼了。
    // 但是需要搞清楚幾個屬性的值灿巧,我在下面代碼中注釋赶袄。
    return SingleRequest.obtain(
        context,
        model,//對應load(myUrl)揽涮,比如一個圖片地址。
        transcodeClass,//轉(zhuǎn)換類型饿肺,這里默認對應Drawable.class
        requestOptions,
        overrideWidth,//寬
        overrideHeight,
        priority,
        target,
        requestListener,
        requestCoordinator,
        context.getEngine(),//全局加載引擎
        transitionOptions.getTransitionFactory());
  }

到這里Request的創(chuàng)建就分析完了蒋困,最終通過RequestBuilder生成了一個SingleRequest實例。這個SingleRequest類中有各種屬性敬辣,大部分都是默認了雪标,當然可以在使用時通過RequestOptions配置。簡單回顧下溉跃。還是在那句代碼貼過來村刨。

Glide.with(fragment).load(myUrl).into(imageView);

咋一看都分析差不多了,但這只是假象撰茎。執(zhí)行到into方法嵌牺,流程剛剛開始,加載龄糊、緩存髓梅、轉(zhuǎn)換等邏輯都在后面。so绎签,我們繼續(xù)枯饿。

執(zhí)行數(shù)據(jù)加載

我們先回到RequestBuilder類中into(Y target)方法,也是執(zhí)行加載的入口诡必。

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
    ... 省略
    requestManager.clear(target);
    target.setRequest(request);
    //跟進manager的track方法
    requestManager.track(target, request);
}

void track(Target<?> target, Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  
  /**
   * Starts tracking the given request.
   */
  public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }
  
  //以下是SingleRequest類中begin方法實現(xiàn)奢方。
  @Override
  public void begin() {
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    //num=0
    if (model == null) {
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }

    //num=3
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

    //num=1
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }

    //num=2
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }

以上代碼分別來至幾個類。很容易看出執(zhí)行流程:into()=>track()=>runRequest()=>begin()爸舒,這里分析下begin方法蟋字。

  • num=0(對應代碼中注釋num=0處)

    可以看到判斷model變量為null,就回調(diào)onLoadFailed方法扭勉,這個方法就會設置我們配置的error placeholder資源鹊奖。這里的model變量就是我們通過load(myUrl)方法傳入的圖片地址。

  • num=1

    這里主要是判斷overrideWidth, overrideHeight是否可用涂炎。分兩種情況:1.如果設置了override(int width, int height) ,直接處理onSizeReady方法邏輯忠聚。2.沒有設置override,Glide就會等到系統(tǒng)計算完組件寬高后再回調(diào)onSizeReady唱捣。所以兩種情況最后都會調(diào)用onSizeReady方法两蟀。

  • num=2

    開始前,回調(diào)設置placeholderDrawable震缭,和num=0類似赂毯。

  • num=3

    加載完成回調(diào)。這里是加載、縮放党涕、轉(zhuǎn)換之后的數(shù)據(jù)烦感,可直接用于UI顯示。后面再分析是怎么回調(diào)刷新UI的膛堤。

到這里手趣,默認流程下一步就會走到onSizeReady方法。

@Override
  public void onSizeReady(int width, int height) {
    ...省略
    //Engine類的load方法骑祟,正式步入加載流程。
    loadStatus = engine.load(
        glideContext,
        model,//對應myUrl,圖片地址
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),//默認是Object.class
        transcodeClass, //默認Drawbale.class
        priority,
        requestOptions.getDiskCacheStrategy(),//緩存策略气笙。默認是AUTOMATIC
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);
  }

可以看到次企,調(diào)用了Engine的load方法。重點來了潜圃。

public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();

    //生成緩存key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);

    //num=4 從內(nèi)存緩存中讀取
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }
    //num=5 如果上一步?jīng)]有命中缸棵,則從ActiveResource中讀取。
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

    //囧谭期,也沒命中堵第。檢查當前Request是否正在執(zhí)行。
    EngineJob<?> current = jobs.get(key);
    if (current != null) {
      //不為null隧出,緩存回調(diào)函數(shù)踏志,等待完成后執(zhí)行。
      current.addCallback(cb);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }
    //num=6 到這里胀瞪,就需要創(chuàng)建后臺任務執(zhí)行了针余。
    EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
        useUnlimitedSourceExecutorPool);
    DecodeJob<R> decodeJob = decodeJobFactory.build(
        glideContext,
        model,
        key,
        signature,
        width,
        height,
        resourceClass,
        transcodeClass,
        priority,
        diskCacheStrategy,
        transformations,
        isTransformationRequired,
        isScaleOnlyOrNoTransform,
        onlyRetrieveFromCache,
        options,
        engineJob);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    //num=7 開啟任務。
    engineJob.start(decodeJob);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }
  • 先說一下Glide的緩存策略

    默認情況下凄诞,Glide 會在開始一個新的圖片請求之前檢查以下多級的緩存:

    1. 活動資源 (Active Resources) - 正在顯示的資源
    2. 內(nèi)存緩存 (Memory cache) - 顯示過的資源
    3. 資源類型(Resource) - 被解碼圆雁、轉(zhuǎn)換后的資源
    4. 數(shù)據(jù)來源 (Data) - 源文件(未處理過)資源

    其實也就是內(nèi)存緩存+磁盤緩存

以上代碼中 注釋:num=4帆谍、5 處伪朽,表示從內(nèi)存緩存中獲取資源,如果命中汛蝙,直接返回烈涮,沒命中則創(chuàng)建任務并執(zhí)行(對應 注釋:num=6、7 )窖剑。本文主要分析工作流程跃脊,緩存部分就不細說了。

我們接著往下看苛吱,DecodeJob內(nèi)部到底做了什么酪术?,可以說DecodeJob是整個流程中的重點,也是我認為設計得很巧妙地方绘雁。

//對應上一段代碼中num=7處橡疼,執(zhí)行EngineJob的start方法
//start方法就是根據(jù)diskCacheStrategy策略獲取一個executor來執(zhí)行DecodeJob
public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //這里根據(jù)緩存策略,決定使用哪個Executor庐舟。默認情況返回diskCacheExecutor欣除。
    //共三種執(zhí)行器:diskCacheExecutor、sourceExecutor挪略、sourceUnlimitedExecutor對應文章前面給出的流程圖历帚。
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

//當然,DecodeJob實現(xiàn)了Runnable接口杠娱。直接來看它的run方法挽牢。
 @Override
  public void run() {
    TraceCompat.beginSection("DecodeJob#run");
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();//看這里!
    } catch (RuntimeException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "DecodeJob threw unexpectedly"
            + ", isCancelled: " + isCancelled
            + ", stage: " + stage, e);
      }
      // When we're encoding we've already notified our callback and it isn't safe to do so again.
      if (stage != Stage.ENCODE) {
        notifyFailed();
      }
      if (!isCancelled) {
        throw e;
      }
    } finally {
      if (currentFetcher != null) {
        currentFetcher.cleanup();
      }
      TraceCompat.endSection();
    }
  }

//接著看runWrapped方法摊求。
//RunReason是一個枚舉禽拔,默認值為INITIALIZE。區(qū)分任務目的室叉。
private void runWrapped() {
     switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }
  
  //獲取任務執(zhí)行階段:初始化睹栖、讀取轉(zhuǎn)換后的緩存、讀取原文件緩存茧痕、原文件加載野来、結(jié)束狀態(tài)。
  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

//根據(jù)上一個方法確定的stage踪旷,創(chuàng)建對應的Generator(可把它簡單理解成資源加載器)
  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        //從轉(zhuǎn)換后的緩存中讀取文件
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
       //從原文件緩存中讀取文件
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
       //沒有緩存梁只,重新加載資源(比如:網(wǎng)絡圖片、本地文件)
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

 //這里開始加載執(zhí)行
  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    //這里Generator.startNext()方法中就是加載過程埃脏,如果成功加載則返回true并跳出循環(huán)搪锣,否則切換Generator繼續(xù)執(zhí)行。
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

     //如果任務執(zhí)行到去加載資源(也就是沒有命中磁盤緩存)彩掐,且切換任務執(zhí)行環(huán)境
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }
 
  @Override
  public void reschedule() {
    //更改執(zhí)行目標為:SOURCE服務构舟。當然也只有在stage == Stage.SOURCE的情況下會被調(diào)用。
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);//這里callback正是EngineJob堵幽。
  } 
  
  //代碼跟進EngineJob類中狗超,可以看到實現(xiàn)方法。
  @Override
  public void reschedule(DecodeJob<?> job) {
    // 可以看到朴下,這里獲取的SourceExecutor來執(zhí)行decodeJob努咐。
    //也就巧妙地將此decodeJob任務從cacheExecutor切換到了SourceExecutor,這樣分工協(xié)作更加高效殴胧。
    getActiveSourceExecutor().execute(job);
  }
 

以上代碼渗稍,為方便理解佩迟,我加了部分注釋。這里再捋一捋思路和邏輯竿屹。從最開始沒有命中內(nèi)存緩存開始报强,然后執(zhí)行Engine的start方法,默認情況會獲取到cacheExecutor執(zhí)行器來執(zhí)行decodeJob任務拱燃;繼續(xù)decodeJob的run方法秉溉,因為RunReason==INITIALIZE,接著獲取stage碗誉,默認會返回Stage.RESOURCE_CACHE,這時通過getNextGenerator就返回了ResourceCacheGenerator加載器召嘶,緊接著就是調(diào)用 ResourceCacheGenerator的startNext方法 ,從轉(zhuǎn)換后的緩存中讀取已緩存的資源哮缺,如果命中則結(jié)束任務并回調(diào)結(jié)果弄跌,反之,任務切換到DataCacheGenerator加載器繼續(xù)執(zhí)行蝴蜓,若還是未命中碟绑,則切換到SourceGenerator加載器(第一次加載俺猿,由于沒有任何緩存茎匠,就會走到這里),這時會通過任務調(diào)度押袍,將線程運行環(huán)境切換到 SourceExecutor執(zhí)行器來執(zhí)行诵冒,最后,待SourceGenerator加載完成后結(jié)束任務谊惭,回調(diào)結(jié)果汽馋,流程結(jié)束。

glide-flow-diagram

現(xiàn)在圈盔,我們再來看看這張流程圖豹芯。是不是已經(jīng)有了一定的理解,不明白之處驱敲,請結(jié)合上文和源碼繼續(xù)研究吧铁蹈。

你可能會問,Glide執(zhí)行流程就分析完了众眨? 加載網(wǎng)絡圖片的代碼邏輯都沒看到拔漳痢? 按照只分析主流程的思路娩梨,點到為止沿腰,以上內(nèi)容就算是分析完了。但相信很多同學都想知道 加載網(wǎng)絡圖片代碼邏輯到底在哪里狈定?Glide是怎么調(diào)用這塊代碼的颂龙? 面對這兩個問題,我們就繼續(xù)吧。

這里就需要從Glide實例初始化開始說起厘托。我們來看下Glide的構(gòu)造方法友雳。

  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  Glide(
      Context context,
      Engine engine,
      MemoryCache memoryCache,
      BitmapPool bitmapPool,
      ArrayPool arrayPool,
      RequestManagerRetriever requestManagerRetriever,
      ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      RequestOptions defaultRequestOptions,
      Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;

    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

    final Resources resources = context.getResources();

    //重點看register類,這里調(diào)用了各種append铅匹、register押赊、prepend等方法。其實就是相關(guān)功能類的注冊過程包斑。
    registry = new Registry();
    registry.register(new DefaultImageHeaderParser());

    Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
        resources.getDisplayMetrics(), bitmapPool, arrayPool);
    ByteBufferGifDecoder byteBufferGifDecoder =
        new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);

    registry.register(ByteBuffer.class, new ByteBufferEncoder())
        .register(InputStream.class, new StreamEncoder(arrayPool))
        /* Bitmaps */
        .append(ByteBuffer.class, Bitmap.class,
            new ByteBufferBitmapDecoder(downsampler))
        .append(InputStream.class, Bitmap.class,
            new StreamBitmapDecoder(downsampler, arrayPool))
        .append(ParcelFileDescriptor.class, Bitmap.class, new VideoBitmapDecoder(bitmapPool))
        .register(Bitmap.class, new BitmapEncoder())
        /* GlideBitmapDrawables */
        .append(ByteBuffer.class, BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, bitmapPool,
                new ByteBufferBitmapDecoder(downsampler)))
        .append(InputStream.class, BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, bitmapPool,
                new StreamBitmapDecoder(downsampler, arrayPool)))
        .append(ParcelFileDescriptor.class, BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, bitmapPool, new VideoBitmapDecoder(bitmapPool)))
        .register(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, new BitmapEncoder()))
        /* GIFs */
        .prepend(InputStream.class, GifDrawable.class,
            new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
        .prepend(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
        .register(GifDrawable.class, new GifDrawableEncoder())
        /* GIF Frames */
        .append(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory<GifDecoder>())
        .append(GifDecoder.class, Bitmap.class, new GifFrameResourceDecoder(bitmapPool))
        /* Files */
        .register(new ByteBufferRewinder.Factory())
        .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
        .append(File.class, InputStream.class, new FileLoader.StreamFactory())
        .append(File.class, File.class, new FileDecoder())
        .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
        .append(File.class, File.class, new UnitModelLoader.Factory<File>())
        /* Models */
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
        .append(
                int.class,
                ParcelFileDescriptor.class,
                new ResourceLoader.FileDescriptorFactory(resources))
        .append(Integer.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
        .append(
                Integer.class,
                ParcelFileDescriptor.class,
                new ResourceLoader.FileDescriptorFactory(resources))
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
        .append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
        .append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
        .append(
                Uri.class,
                ParcelFileDescriptor.class,
                new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
        .append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
        .append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
        .append(
            Uri.class,
             InputStream.class,
             new UriLoader.StreamFactory(context.getContentResolver()))
        .append(Uri.class, ParcelFileDescriptor.class,
             new UriLoader.FileDescriptorFactory(context.getContentResolver()))
        .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
        .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
        .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
        .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
        /* Transcoders */
        .register(Bitmap.class, BitmapDrawable.class,
            new BitmapDrawableTranscoder(resources, bitmapPool))
        .register(Bitmap.class, byte[].class, new BitmapBytesTranscoder())
        .register(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder());

    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext =
        new GlideContext(
            context, registry, imageViewTargetFactory, defaultRequestOptions,
            defaultTransitionOptions, engine, logLevel);
  }

以上代碼可以看出流礁,完成對registry對象的各種功能類注冊(這種設計提高了其擴展性),太多了有木有眼花罗丰,各種loader神帅、encoder、decoder萌抵、transcoder等等找御,帶著問題我們只分析和網(wǎng)絡圖片加載相關(guān)的。

.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

以上代碼可以看出绍填,分別對String.class霎桅、Uri.class、GlideUrl.class三種類型注入了不同的Factory讨永,這個Factory使用創(chuàng)建ModelLoader的滔驶,ModelLoader就是用來加載圖片的。說的不是很清楚卿闹,這里只需要明白揭糕,registry分別對這三種類型注冊了生成ModelLoader的工廠類

接著锻霎,進入Registry類中著角,看看怎么緩存這些功能類的。

public class Registry {
  //各種功能類注冊器旋恼。加載吏口、轉(zhuǎn)換、解碼蚌铜、加密等锨侯。
  private final ModelLoaderRegistry modelLoaderRegistry;
  private final EncoderRegistry encoderRegistry;
  private final ResourceDecoderRegistry decoderRegistry;
  private final ResourceEncoderRegistry resourceEncoderRegistry;
  private final DataRewinderRegistry dataRewinderRegistry;
  private final TranscoderRegistry transcoderRegistry;
  private final ImageHeaderParserRegistry imageHeaderParserRegistry;
  
    ...
     modelLoader注冊
     public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
          ModelLoaderFactory<Model, Data> factory) {
        modelLoaderRegistry.append(modelClass, dataClass, factory);
        return this;
      }
  
    ...
  
  }
  
  //繼續(xù)跟進代碼。ModelLoaderRegistry類中
  public synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory) {
    multiModelLoaderFactory.append(modelClass, dataClass, factory);
    cache.clear();
  }
  
  //最后進入MultiModelLoaderFactory類中的add方法
  private <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory, boolean append) {
    Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
    //entries是一個list冬殃。所以囚痴,到這里就知道注冊的LoaderFactory被緩存到了列表中,以便后面取用审葬。
    entries.add(append ? entries.size() : 0, entry);
  }
  

通過以上代碼分析深滚,知道了ModelLoaderFactory在Glide實例化時被注冊到了一個列表中,以待用時獲取奕谭。 在分析DecodeJob代碼邏輯時,我們知道SourceGenerator是加載圖片資源的痴荐,下面我們就看下SourceGenerator是怎么獲取上面注冊的ModelLoader并完成數(shù)據(jù)加載的血柳。

  //DecodeJob調(diào)用SourceGenerator的startNext方法執(zhí)行任務。
  @Override
  public boolean startNext() {
    //忽略緩存部分邏輯
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //num=8
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //num=9 這里是真正調(diào)用fetcher.loadData方法加載數(shù)據(jù)的地方生兆。
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

以上代碼 num=9 注釋處难捌,可以看出loadData對象是通過helper.getLoadData()返回并在while中條件篩選得到。接著看下這個helper類是什么來頭鸦难。

首先DecodeHelper是在DecodeJob中實例化的根吁。

//DecodeJob類中,decodeHelper調(diào)用init方式的代碼片段
decodeHelper.init(
        glideContext,
        model, //String類型合蔽,對應load(myUrl)中的myUrl.
        signature,
        width,
        height,
        diskCacheStrategy,
        resourceClass, //默認是Object.class
        transcodeClass, //默認是Drawable.class
        priority,
        options,
        transformations,
        isTransformationRequired,
        isScaleOnlyOrNoTransform,
        diskCacheProvider);

知道m(xù)odel變量的類型击敌,對獲取ModelLoader邏輯理解很重要。現(xiàn)在我們?nèi)ecodeHelper類中查看getLoadData方法拴事。(對應上一個代碼塊中 num=8注釋處

List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      //根據(jù)model類型沃斤,通過Glide對應Registry獲取ModelLoader列表。
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      int size = modelLoaders.size();
      for (int i = 0; i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current =
            modelLoader.buildLoadData(model, width, height, options);
            //循環(huán)創(chuàng)建出LoadData刃宵,用戶后面加載衡瓶。
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }
  //Registry類中g(shù)etModelLoaders方法,沒什么說的组去。
  //但要知道m(xù)odelLoaderRegistry早在Glide實例化時已注冊了所有ModelLoaderFactory就行鞍陨。
  public <Model> List<ModelLoader<Model, ?>> getModelLoaders(Model model) {
    List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
    if (result.isEmpty()) {
      throw new NoModelLoaderAvailableException(model);
    }
    return result;
  }
  
  //進入ModelLoaderRegistry類
  public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(A model) {
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
    int size = modelLoaders.size();
    List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
      ModelLoader<A, ?> loader = modelLoaders.get(i);
      if (loader.handles(model)) {
        filteredLoaders.add(loader);
      }
    }
    return filteredLoaders;
  }
  
  //將modelClass(默認是String.class)參數(shù)步淹,通過multiModelLoaderFactory.build方法創(chuàng)建出ModelLoader从隆。
  //可以看出,這里loader也是做了緩存的缭裆,避免重復build對象键闺。
  private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
    List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
    if (loaders == null) {
      loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
      cache.put(modelClass, loaders);
    }
    return loaders;
  }

  @SuppressWarnings("unchecked")
  private static <A> Class<A> getClass(A model) {
    return (Class<A>) model.getClass();
  }

到這里,我們再理一下思路澈驼。資源加載器SourceGenerator辛燥,使用特定model類型,通過Register獲取已經(jīng)注冊過的ModelLoader列表缝其,當然這個ModelLoader是通過注冊的xxxFactory.build而來挎塌,拿到ModelLoader列表后,在通過modelLoader.buildLoadData方法轉(zhuǎn)化為LoadData對象列表内边,LoadData對象持有一個DataFetcher引用榴都,最后就是在加載器SourceGenerator中調(diào)用以下代碼執(zhí)行加載數(shù)據(jù)。

loadData.fetcher.loadData(helper.getPriority(), this);

再來看之前注入的三個loaderFactory

.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

告訴大家漠其,通過Glide.with(this).load(myUrl).into(view)嘴高,真正加載網(wǎng)絡圖片對應的loader是HttpGlideUrlLoader竿音。先來看下為什么是它?明明傳入的是String而非GlideUrl拴驮。

按照類型注冊春瞬,那匹配會先獲取到StringLoader.StreamFactory。

public class StringLoader<Data> implements ModelLoader<String, Data> {
  private final ModelLoader<Uri, Data> uriLoader;

  public StringLoader(ModelLoader<Uri, Data> uriLoader) {
    this.uriLoader = uriLoader;
  }

  @Override
  public LoadData<Data> buildLoadData(String model, int width, int height,
      Options options) {
    Uri uri = parseUri(model);
    return uri == null ? null : uriLoader.buildLoadData(uri, width, height, options);
  }

  
  /**
   * Factory for loading {@link InputStream}s from Strings.
   */
  public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {

    @Override
    public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
      //num=10
      return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
    }

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}

num=10 注釋處套啤,MultiModelLoaderFactory通過Uri.class和InputStream.class創(chuàng)建一個ModelLoader給StringLoader宽气,所以StringLoader的加載功能轉(zhuǎn)移了。而且根據(jù)注冊關(guān)系知道轉(zhuǎn)移到了HttpUriLoader中潜沦。

public class HttpUriLoader implements ModelLoader<Uri, InputStream> {
  private static final Set<String> SCHEMES =
      Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));

  private final ModelLoader<GlideUrl, InputStream> urlLoader;

  public HttpUriLoader(ModelLoader<GlideUrl, InputStream> urlLoader) {
    this.urlLoader = urlLoader;
  }

  @Override
  public LoadData<InputStream> buildLoadData(Uri model, int width, int height, Options options) {
    return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
  }

  @Override
  public boolean handles(Uri model) {
    return SCHEMES.contains(model.getScheme());
  }

  /**
   * Factory for loading {@link InputStream}s from http/https {@link Uri}s.
   */
  public static class Factory implements ModelLoaderFactory<Uri, InputStream> {

    @Override
    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      //num=11 MD又轉(zhuǎn)移了抹竹,這次轉(zhuǎn)移到了HttpGlideUrlLoader
      return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
    }

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}

//跟進HttpGlideUrlLoader類
public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
  /**
   * An integer option that is used to determine the maximum connect and read timeout durations (in
   * milliseconds) for network connections.
   *
   * <p>Defaults to 2500ms.
   */
  public static final Option<Integer> TIMEOUT = Option.memory(
      "com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500);

  @Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;

  public HttpGlideUrlLoader() {
    this(null);
  }

  public HttpGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
    this.modelCache = modelCache;
  }

  @Override
  public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
      Options options) {
    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
    // spent parsing urls.
    GlideUrl url = model;
    if (modelCache != null) {
      url = modelCache.get(model, 0, 0);
      if (url == null) {
        modelCache.put(model, 0, 0, model);
        url = model;
      }
    }
    int timeout = options.get(TIMEOUT);
    //創(chuàng)建LoadData,新建的HttpUrlFetcher
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

  @Override
  public boolean handles(GlideUrl model) {
    return true;
  }

  /**
   * The default factory for {@link HttpGlideUrlLoader}s.
   */
  public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
    private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<>(500);

    @Override
    public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
      //可以看出HttpGlideUrlLoader打算自己處理
      return new HttpGlideUrlLoader(modelCache);
    }

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}

到這里就知道了止潮,load(myUrl) 實際加載是使用的HttpGlideUrlLoader窃判,對應的Fetcher就是HttpUrlFetcher

最后貼下HttpUrlFetcher代碼

/**
 * A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
 */
public class HttpUrlFetcher implements DataFetcher<InputStream> {

  public HttpUrlFetcher(GlideUrl glideUrl, int timeout) {
    this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);
  }

  // Visible for testing.
  HttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {
    this.glideUrl = glideUrl;
    this.timeout = timeout;
    this.connectionFactory = connectionFactory;
  }

  @Override
  public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    final InputStream result;
    try {
      result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
          glideUrl.getHeaders());
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
      return;
    }

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
          + " ms and loaded " + result);
    }
    callback.onDataReady(result);
  }

  private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {
      throw new HttpException("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 HttpException("In re-direct loop");

        }
      } catch (URISyntaxException e) {
        // Do nothing, this is best effort.
      }
    }

    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);

    // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
    // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
    urlConnection.setInstanceFollowRedirects(false);

    // 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 HttpException("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 HttpException(statusCode);
    } else {
      throw new HttpException(urlConnection.getResponseMessage(), statusCode);
    }
  }

  private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
      throws IOException {
    if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
      int contentLength = urlConnection.getContentLength();
      stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
    } else {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
      }
      stream = urlConnection.getInputStream();
    }
    return stream;
  }

}

歷經(jīng)千山萬水喇闸,袄琳,終于看到了網(wǎng)絡通訊代碼。比較簡單燃乍,就是通過HttpURLConnection獲取數(shù)據(jù)流并返回唆樊。當然你也可以使用Okhttp來加載,具體用法請查詢官網(wǎng)刻蟹。

到這里逗旁,已經(jīng)分析完Glide的整個加載過程,剩下就簡單說下回調(diào)刷新UI部分舆瘪。

回調(diào)刷新UI

回到DecodeJob類中

@Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
      ...
      //資源加載完成后回調(diào)片效,執(zhí)行
      decodeFromRetrievedData();
      ...
  }
  /處理源數(shù)據(jù)
  private void decodeFromRetrievedData() {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
          + ", cache key: " + currentSourceKey
          + ", fetcher: " + currentFetcher);
    }
    Resource<R> resource = null;
    try {
      //比如縮放,轉(zhuǎn)換等英古。會判斷currentDataSource類型淀衣。
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      exceptions.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }
    //通過完成,并回調(diào)召调。
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      //磁盤緩存
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
      onEncodeComplete();
    }
  }
  
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    //這里的callback就是EngineJob
    callback.onResourceReady(resource, dataSource);
  }

以上代碼看出,DecodeJob加載完數(shù)據(jù)后膨桥,會做轉(zhuǎn)換、緩存等操作唠叛,這些咱不細究只嚣,關(guān)注回調(diào)流程即可。

//來到EngineJob類中
@Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    //將回調(diào)過程通過Handler切換到主線程
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  }
  
   @Override
    public boolean handleMessage(Message message) {
      EngineJob<?> job = (EngineJob<?>) message.obj;
      switch (message.what) {
        case MSG_COMPLETE:
          job.handleResultOnMainThread();
          break;
        case MSG_EXCEPTION:
          job.handleExceptionOnMainThread();
          break;
        case MSG_CANCELLED:
          job.handleCancelledOnMainThread();
          break;
        default:
          throw new IllegalStateException("Unrecognized message: " + message.what);
      }
      return true;
    }

  @Synthetic
  void handleResultOnMainThread() {
    stateVerifier.throwIfRecycled();
    if (isCancelled) {
      resource.recycle();
      release(false /*isRemovedFromQueue*/);
      return;
    } else if (cbs.isEmpty()) {
      throw new IllegalStateException("Received a resource without any callbacks to notify");
    } else if (hasResource) {
      throw new IllegalStateException("Already have resource");
    }
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;

    // Hold on to resource for duration of request so we don't recycle it in the middle of
    // notifying if it synchronously released by one of the callbacks.
    engineResource.acquire();
    //通知并緩存到activeResources(內(nèi)存緩存中的一種)中艺沼。
    listener.onEngineJobComplete(key, engineResource);

    for (ResourceCallback cb : cbs) {
      if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        //這里將回調(diào)到SingleRequest的onResourceReady中册舞。
        cb.onResourceReady(engineResource, dataSource);
      }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();

    release(false /*isRemovedFromQueue*/);
  }
  
  //繼續(xù)跟進SingleRequest類
  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    ...
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }

  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;
   
    if (requestListener == null
        || !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource)) {
      Transition<? super R> animation =
          animationFactory.build(dataSource, isFirstResource);
    //如果沒有設置requestListener或者未消耗事件,就會回調(diào)target的onResourceReady方法澳厢。
    //默認的target是DrawableImageViewTarget     
      target.onResourceReady(result, animation);
    }

    notifyLoadSuccess();
  }
  

從以上代碼得知环础,回調(diào)從Decodejob出來囚似,在EngineJob中切換到主線程并一路回調(diào)到DrawableImageViewTarget中,至于為什么默認是DrawableImageViewTarget线得,請查看RequestBuilder中into方法饶唤。下面我們再看下DrawableImageViewTarget相關(guān)代碼,也是設置顯示圖片的地方贯钩。

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  public DrawableImageViewTarget(ImageView view) {
    super(view);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    //實現(xiàn)了該方法募狂。簡單將drawable設置給imageview饶号。
    view.setImageDrawable(resource);
  }
}

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter {

  public ImageViewTarget(ImageView view) {
    super(view);
  }

  @Override
  public void onResourceReady(Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }

  private void setResourceInternal(@Nullable Z resource) {
    maybeUpdateAnimatable(resource);
    setResource(resource);
  }

  private void maybeUpdateAnimatable(@Nullable Z resource) {
    if (resource instanceof Animatable) {
      animatable = (Animatable) resource;
      animatable.start();
    } else {
      animatable = null;
    }
  }

  protected abstract void setResource(@Nullable Z resource);
}

可以看到寄症,onResourceReady在父類ImageViewTarget中回調(diào),然后調(diào)用setResource將圖片設置并顯示出來停蕉。代碼執(zhí)行到這里勺三,回調(diào)過程也就完了雷滚。Glide的整個執(zhí)行流程也分析完了。

總結(jié)

圖片加載相關(guān)開源項目也有好幾個(Picasso吗坚、Fresco祈远、ImageLoader),為啥Glide能占一席之地商源。有幾點:1.Google 推薦车份, 2. 專注平滑的滾動, 3. 簡單易用的API 牡彻, 4.高性能扫沼、可擴展。

經(jīng)過這段時間的學習庄吼,我發(fā)現(xiàn)缎除,Glide先通過簡單易用吸引你,閱讀源碼后發(fā)現(xiàn)其功能之強大霸褒,所以可見代碼設計伴找、封裝均是上佳之作盈蛮;然后又發(fā)現(xiàn)废菱,其功能擴展也能隨心所欲,這里推薦一個圖片轉(zhuǎn)換庫glide-transformations抖誉;源碼中殊轴,我比較欣賞Decodejob類相關(guān)部分設計,功能無縫切換袒炉,線程調(diào)度自然旁理。總之我磁,Glide源碼值得你用心拜讀孽文。

雜談

  • 文章太長

    雖然一直在強調(diào)只分析主流程驻襟,不關(guān)心細節(jié),但還是寫了怎么多芋哭,囧~沉衣。后面得注意了,畢竟長篇幅减牺,問題難免考慮周全豌习,也不易于閱讀。

  • RTFSC (Read the fucking source code )

    遇到問題拔疚,第一時間想到的應該是閱讀源碼肥隆。

  • 將知識點與自己連接起來

    我以前一直有個問題,看到喜歡的文章就會收藏稚失,想的是后面慢慢看栋艳,或者用到的時候能找到就行;但久了發(fā)現(xiàn)收藏的文章基本和自己沒啥關(guān)系句各,什么用到的時候能找到嘱巾?別想,基本能全忘記诫钓,啥印象都沒旬昭。但現(xiàn)在看到好文章,我至少得評論下菌湃,甚至花時間梳理问拘,這樣對知識點就有了連接,才有可能日后為你所用惧所。

看到這里了骤坐,好厲害∠掠快給自己和文章點個贊吧纽绍。

限于水平有限,文中定有錯誤和疏漏之處势似,懇請賜教拌夏。若有不明白之處,歡迎隨時評論交流履因。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市毅舆,隨后出現(xiàn)的幾起案子西篓,更是在濱河造成了極大的恐慌愈腾,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岂津,死亡現(xiàn)場離奇詭異顶滩,居然都是意外死亡,警方通過查閱死者的電腦和手機寸爆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門礁鲁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赁豆,你說我怎么就攤上這事仅醇。” “怎么了魔种?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵析二,是天一觀的道長。 經(jīng)常有香客問我节预,道長叶摄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任安拟,我火速辦了婚禮蛤吓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘糠赦。我一直安慰自己会傲,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布拙泽。 她就那樣靜靜地躺著淌山,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顾瞻。 梳的紋絲不亂的頭發(fā)上泼疑,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音荷荤,去河邊找鬼退渗。 笑死,一個胖子當著我的面吹牛梅猿,可吹牛的內(nèi)容都是我干的氓辣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼袱蚓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了几蜻?” 一聲冷哼從身側(cè)響起喇潘,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤体斩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颖低,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體絮吵,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年忱屑,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹬敲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡莺戒,死狀恐怖伴嗡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情从铲,我是刑警寧澤瘪校,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站名段,受9級特大地震影響阱扬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伸辟,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一麻惶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧信夫,春花似錦用踩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姊扔,卻和暖如春惠奸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恰梢。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工佛南, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嵌言。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓嗅回,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摧茴。 傳聞我的和親對象是個殘疾皇子绵载,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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