Glide源碼流程分析

1. Glide總覽

Glide圖片加載框架從總體來看采用三段論來讓用戶輕松加載圖片腥光。

  • with()方法主要通過創(chuàng)建一個(gè)空白的Fragment蓋在展示的頁面上笆搓,感知展示頁面的生命周期,來動(dòng)態(tài)調(diào)整Glide內(nèi)部請(qǐng)求等業(yè)務(wù)去扣。詳細(xì)參見Glide源碼分析-生命周期管理
  • load()方法完成通用請(qǐng)求的構(gòu)建
  • into()方法主要維護(hù)這運(yùn)行隊(duì)列和等待隊(duì)列,當(dāng)生命周期改變的時(shí)候藏姐,動(dòng)態(tài)清除一些不必要加載的請(qǐng)求。同時(shí)還維護(hù)著活動(dòng)緩存和內(nèi)存緩存该贾,多級(jí)緩存有利于節(jié)約資源和加載效率羔杨,最后如果沒有緩存就采用網(wǎng)絡(luò)請(qǐng)求的方式加載請(qǐng)求的內(nèi)容。
Glide總覽.png

2. with()

with()方法的詳細(xì)分析可以參見上一篇Glide的敘述:Glide源碼分析-生命周期管理杨蛋。

Glide_with.png

總體來講兜材,with()方法中發(fā)生了如下事情:

  • Glide類:主要做一些 init 工作,比如緩存逞力,線程池曙寡,復(fù)用池的構(gòu)建等等。
  • RequestManagerRetriever類:主要是獲得一個(gè) RequestManager 請(qǐng)求管理類寇荧,然后綁定一個(gè) Fragment
  • SupportRequestManagerFragment 類:用于管理請(qǐng)求的生命周期举庶。
  • RequestManager:主要用于對(duì)請(qǐng)求的管理封裝。

3. load()

load()流程其實(shí)不是很復(fù)雜揩抡,主要做了通用資源類型的設(shè)置選項(xiàng)和啟動(dòng)負(fù)載户侥,在RequestBuilder 中完成。流程如下圖:

Glide_with_load.png

RequestBuilder : 這是一個(gè)通用請(qǐng)求構(gòu)建類峦嗤,可以處理通用資源類型的設(shè)置選項(xiàng)和啟動(dòng)負(fù)載蕊唐。

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
      
  .....
    
  public RequestBuilder<Drawable> load(@Nullable String string) {
    //這里調(diào)用 Drawable 圖片加載請(qǐng)求器為其加載
    return asDrawable().load(string);
  }
  
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }


  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }
    
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }         
}
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
        
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  // 描述加載的數(shù)據(jù)源-這里可以看做是我們傳遞進(jìn)來的 http://xxxx.png
  @Nullable private Object model;
  // 描述這個(gè)請(qǐng)求是否已經(jīng)添加了加載的數(shù)據(jù)源
  private boolean isModelSet;
  
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }
  
  // model是我們需要加載的數(shù)據(jù)源,可能為null烁设,為了保證model一定被賦值替梨,此處引進(jìn)isModelSet來保證。
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
      this.model = model;
      isModelSet = true;
      return this;
  }
}

到這里 RequestBuilder 就構(gòu)建好了装黑, RequestBuilder構(gòu)建出來副瀑,接下來就是重頭戲into()。

4.into()

into()是所有流程中最最復(fù)雜的方法恋谭,有60多個(gè)步驟糠睡,此處我們分成幾個(gè)環(huán)節(jié)來一一分析流程。

分析之前箕别,我們腦子中一定記住從最初是狀態(tài)開始翻代碼铜幽,最初始狀態(tài)就是:拿到了url滞谢,各級(jí)緩存都沒有資源串稀,需要從網(wǎng)上下載資源,然后加載到控件中狮杨。

into()整體流程圖在Glide工程中src目錄中母截。

4.1 確定控件大小

我們從網(wǎng)上加載的inputstram不可能是下載多少就加載多少,這樣內(nèi)存可能爆掉橄教,或者效率很低清寇,我們應(yīng)該按需加載喘漏,控件多大,我們就將圖片壓縮到多大加載华烟。

確定控件大小.png
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    // 根據(jù) ImageView 布局中的 scaleType 來重構(gòu) requestOptions
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      //如果在 xml ImageView 節(jié)點(diǎn)中 沒有設(shè)置 scaleType 那么默認(rèn)在構(gòu)造函數(shù)中進(jìn)行了初始化為   mScaleType = ScaleType.FIT_CENTER;  
      switch (view.getScaleType()) {
       .....
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          //這里用到了克卖媛酢(原型設(shè)計(jì)模式),選擇一個(gè)"居中合適"顯示的方案
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
       ....
      }
    }
    //調(diào)用 into 重載函數(shù)盔夜,創(chuàng)建一個(gè) ViewTarget
    return into(
        //調(diào)用 buildImageViewTarget 構(gòu)建一個(gè) ImageView 類型的 Target(Bitmap/Drawable)
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

上面代碼就兩大步:

第一步:先拿到當(dāng)前 ImageView getScaleType 類型的屬性负饲,然后重新 clone 一個(gè)進(jìn)行配置;

第二步:調(diào)用 into 重載繼續(xù)構(gòu)建喂链;

先來看下 glideContext.buildImageViewTarget 是怎么構(gòu)建出來 ImageViewTarget 的:

  @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    //調(diào)用 工廠模式 根據(jù) transcodeClass 生成出一個(gè)對(duì)應(yīng)的 ImageViewTarget
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    //如果目標(biāo)的編碼類型屬于 Bitmap 那么就創(chuàng)建一個(gè) Bitmap 類型的 ImageViewTarget
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    ////如果目標(biāo)的編碼類型屬于 Drawable 那么就創(chuàng)建一個(gè) Drawable 類型的 ImageViewTarget
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}

上面 生產(chǎn) Target 的時(shí)候注意一下返十,只要調(diào)用了 asBitmap 才會(huì)執(zhí)行生產(chǎn) BitmapImageViewTarget ,所以這里我們關(guān)注 Drawable 類型就行了,我們就先簡單看看這個(gè) target 內(nèi)部怎么實(shí)現(xiàn)的椭微,因?yàn)樽詈螽?dāng)拿到請(qǐng)求資源之后洞坑,會(huì)在target中設(shè)置資源到view中。

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

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

  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
}

從上面代碼可以知道 DrawableImageViewTarget 繼承的是 ImageViewTarget 重寫的 setResource 函數(shù)蝇率,實(shí)現(xiàn)了顯示 Drawable 圖片的邏輯迟杂。

回到主流程,into()重載方法中本慕。

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    //這里的 isModelSet 是在 load 的時(shí)候賦值為 true 的逢慌,所以不會(huì)拋異常,確保了信息源一定被設(shè)置间狂。
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
        //為這個(gè) http://xxx.png 生成一個(gè) Glide request 請(qǐng)求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
        //相當(dāng)于拿到上一個(gè)請(qǐng)求
    Request previous = target.getRequest();
    //下面的幾行說明是否與上一個(gè)請(qǐng)求沖突攻泼,一般不用管 直接看下面 else 判斷
    // 如果兩個(gè)請(qǐng)求參數(shù)和大小相同,就不用再請(qǐng)求了鉴象,直接返回忙菠。此if中也會(huì)判斷前一個(gè)請(qǐng)求是否在請(qǐng)求中等狀態(tài),此處我們不用關(guān)心纺弊,第一次加載圖片不會(huì)進(jìn)入此if語句牛欢。
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
    //如果是一個(gè)新請(qǐng)求,先清理掉目標(biāo)請(qǐng)求管理
    requestManager.clear(target);
    //重新為目標(biāo)設(shè)置一個(gè) Glide request 請(qǐng)求
    target.setRequest(request);
    //最后是調(diào)用 RequestManager 的 track 來執(zhí)行目標(biāo)的 Glide request 請(qǐng)求
    requestManager.track(target, request);

    return target;
  }

以上核心就兩個(gè)點(diǎn):

第一點(diǎn):為 target buildRequest 構(gòu)建一個(gè) Glide request 請(qǐng)求淆游; 

第二點(diǎn):將構(gòu)建出來的 Request 交于 RequestManager 來執(zhí)行傍睹;

我們先簡單的來看下怎么構(gòu)建的 Request:

  private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions,
        callbackExecutor);
  }
  private Request obtainRequest(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      Executor callbackExecutor) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }

最后我們發(fā)現(xiàn)是 SingleRequest.obtain 來為我們構(gòu)建的 Request 請(qǐng)求對(duì)象,開始只是初始化一些配置屬性犹菱。

繼續(xù)回到上面看request的track()方法蚌堵。

//這里對(duì)當(dāng)前 class 加了一個(gè)同步鎖避免線程引起的安全性  
 synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    //添加一個(gè)目標(biāo)任務(wù)  
    targetTracker.track(target);
    //執(zhí)行 Glide request
    requestTracker.runRequest(request);
  }
public void runRequest(@NonNull Request request) {
    //添加一個(gè)請(qǐng)求
    requests.add(request);
    //是否暫停
    if (!isPaused) {
      //沒有暫停,開始調(diào)用 Request begin 執(zhí)行
      request.begin();
    } else {
      //如果調(diào)用了 暫停宁炫,清理請(qǐng)求
      request.clear();
      pendingRequests.add(request);
    }
}

上面的邏輯是先為 requests 添加一個(gè)請(qǐng)求乏悄,看看是否是停止?fàn)顟B(tài),如果不是就調(diào)用 request.begin();執(zhí)行陕凹。

這里的 Request 是一個(gè)接口悍抑,通過之前我們講到 buildRequest 函數(shù)可知 Request 的實(shí)現(xiàn)類是 SingleRequest 我們就直接看它的 begin 函數(shù).

 @Override
  public synchronized void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      //檢查外部調(diào)用的尺寸是否有效
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      //失敗的回調(diào)
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }

    if (status == Status.COMPLETE) {
      //表示資源準(zhǔn)備好了
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }


    status = Status.WAITING_FOR_SIZE;
    //這里表示大小已經(jīng)準(zhǔn)備好了
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      // 需要加載的尺寸已經(jīng)測(cè)量好了鳄炉,接下里就開始加載引擎。
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }
        
    //這里是剛剛開始執(zhí)行的回調(diào)搜骡,相當(dāng)于顯示開始的進(jìn)度
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }
@Override
  public void onSizeReady(int width, int height) {
    ......
      loadStatus =
          engine.load(
              glideContext,
              model,
              requestOptions.getSignature(),
              this.width,
              this.height,
              requestOptions.getResourceClass(),
              transcodeClass,
              priority,
              requestOptions.getDiskCacheStrategy(),
              requestOptions.getTransformations(),
              requestOptions.isTransformationRequired(),
              requestOptions.isScaleOnlyOrNoTransform(),
              requestOptions.getOptions(),
              requestOptions.isMemoryCacheable(),
              requestOptions.getUseUnlimitedSourceGeneratorsPool(),
              requestOptions.getUseAnimationPool(),
              requestOptions.getOnlyRetrieveFromCache(),
              this,
              callbackExecutor);

      ......
    }
  }

4.2 從不同緩存中搜尋資源

從不同緩存中獲取資源過程.png
public synchronized <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 useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb,
      Executor callbackExecutor) {

    //拿到緩存或者請(qǐng)求的 key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
        //根據(jù) key 拿到活動(dòng)緩存中的資源
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    //如果 ActiveResources 活動(dòng)緩存中有就回調(diào)出去
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }

    //嘗試從 LruResourceCache 中找尋這個(gè)資源 
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      //如果內(nèi)存緩存 Lru 中資源存在回調(diào)出去
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }

    //------------- 走到這里說明活動(dòng)緩存 跟內(nèi)存 緩存都沒有找到 -----------
    
    //根據(jù) Key 看看緩存中是否正在執(zhí)行
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      //如果正在執(zhí)行拂盯,把數(shù)據(jù)回調(diào)出去
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

    // --------------   走到這里說明是一個(gè)新的任務(wù)  ---------------
    // --------------   構(gòu)建新的請(qǐng)求任務(wù)  ---------------
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);
        //把當(dāng)前需要執(zhí)行的 key 添加進(jìn)緩存
    jobs.put(key, engineJob);
        //執(zhí)行任務(wù)的回調(diào)
    engineJob.addCallback(cb, callbackExecutor);
    //開始執(zhí)行。
    engineJob.start(decodeJob);

    return new LoadStatus(cb, engineJob);
  }

通過 engine.load 這個(gè)函數(shù)里面的邏輯记靡,我們可以總結(jié)3點(diǎn):

  1. 先構(gòu)建請(qǐng)求或者緩存 KEY ;

  2. 根據(jù) KEY 從內(nèi)存緩存中查找對(duì)應(yīng)的資源數(shù)據(jù)(ActiveResources(活動(dòng)緩存磕仅,內(nèi)部是一個(gè) Map 用弱引用持有),LruResourceCache),如果有就回調(diào) 對(duì)應(yīng)監(jiān)聽的 onResourceReady 表示數(shù)據(jù)準(zhǔn)備好了簸呈。

  3. 從執(zhí)行緩存中查找對(duì)應(yīng) key 的任務(wù)

    1. 如果找到了榕订,就說明已經(jīng)正在執(zhí)行了,不用重復(fù)執(zhí)行蜕便。

    2. 沒有找到劫恒,通過 EngineJob.start 開啟一個(gè)新的請(qǐng)求任務(wù)執(zhí)行。

為了看主線轿腺,時(shí)刻記住開始強(qiáng)調(diào)的两嘴,所有緩存中都沒有數(shù)據(jù)!直接看 EngineJob.start

public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //拿到 Glide 執(zhí)行的線程池
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    //開始執(zhí)行
    executor.execute(decodeJob);
  }

通過 DecodeJob 源碼得知族壳,它是實(shí)現(xiàn)的 Runnable 接口憔辫,這里 GlideExecutor 線程池開始執(zhí)行,就會(huì)啟動(dòng) DecodeJob 的 run 函數(shù)仿荆。

細(xì)心的小伙伴可以看出engineJob之后就decodeJob贰您,不難想出,這兩個(gè)任務(wù)之間一定有獲取到資源的過程拢操。而decodeJob包裹了engineJob锦亦,decodeJob是一個(gè)runnable,所以不難猜出會(huì)在線程池中網(wǎng)絡(luò)請(qǐng)求資源令境,并且解碼資源杠园。下面我們就來看看是如何加載資源和解碼的。

4.3 尋找資源加載器

此處不得不佩服框架考慮得的確很周全舔庶,我們需要加載資源抛蚁,肯定就需要資源加載器,那么是不是只有一個(gè)加載器呢惕橙,當(dāng)然不是瞧甩,只有一個(gè)加載器的代碼可能是我寫的,哈哈哈吕漂。透露小秘密:框架會(huì)根據(jù)我們load傳入的不同信息源亲配,獲取不同的資源加載器尘应。下面我們來看一下流程惶凝。

獲取url加載器.png

上一小節(jié)結(jié)束我們可以看到包裹了engineJob的decodeJob任務(wù)會(huì)在線程池中執(zhí)行吼虎,那么不難想出decodeJob的run()肯定會(huì)被調(diào)用。

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      
      // 線程執(zhí)行調(diào)用 run
      @Override
      public void run() {

        GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);

        DataFetcher<?> localFetcher = currentFetcher;
        try {
          //是否取消了當(dāng)前請(qǐng)求
          if (isCancelled) {
            notifyFailed();
            return;
          }
          //執(zhí)行
          runWrapped();
        } catch (CallbackException e) {

         .....//一些錯(cuò)誤回調(diào)
      }
    }

分析runWrapped:

private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        //獲取資源狀態(tài)
        stage = getNextStage(Stage.INITIALIZE);
        //根據(jù)當(dāng)前資源狀態(tài)苍鲜,獲取資源執(zhí)行器
        currentGenerator = getNextGenerator();
        //執(zhí)行
        runGenerators();
        break;
      ...
    }
  }

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        //如果外部調(diào)用配置了資源緩存策略思灰,那么返回 Stage.RESOURCE_CACHE
        //否則繼續(xù)調(diào)用 Stage.RESOURCE_CACHE 執(zhí)行。
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        //如果外部配置了源數(shù)據(jù)緩存混滔,那么返回 Stage.DATA_CACHE
        //否則繼續(xù)調(diào)用 getNextStage(Stage.DATA_CACHE)
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        //如果只能從緩存中獲取數(shù)據(jù)洒疚,則直接返回 FINISHED,否則坯屿,返回SOURCE油湖。
        //意思就是一個(gè)新的資源
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

通過上面代碼可以知道,我們?cè)谡屹Y源的執(zhí)行器领跛,這里由于我們沒有在外部配置緩存策略所以乏德,直接從源數(shù)據(jù)加載,看下面代碼:

private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      //從資源緩存執(zhí)行器
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      //源數(shù)據(jù)磁盤緩存執(zhí)行器
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      //什么都沒有配置吠昭,源數(shù)據(jù)的執(zhí)行器
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

由于我們什么都沒有配置喊括,返回的是 SourceGenerator 源數(shù)據(jù)執(zhí)行器。繼續(xù)下面代碼執(zhí)行:

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    //判斷是否取消矢棚,是否開始
    //調(diào)用 DataFetcherGenerator.startNext() 判斷是否是屬于開始執(zhí)行的任務(wù)
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
   
      ....
  }

上面代碼先看 currentGenerator.startNext() 這句代碼郑什,DataFetcherGenerator 是一個(gè)抽象類,那么這里執(zhí)行的實(shí)現(xiàn)類是哪一個(gè)蒲肋,可以參考下面說明:

狀態(tài)標(biāo)記 作用 執(zhí)行器
Stage.RESOURCE_CACHE 從磁盤中獲取緩存的資源數(shù)據(jù) ResourceCacheGenerator
Stage.DATA_CACHE 從磁盤中獲取緩存的源數(shù)據(jù) DataCacheGenerator
Stage.SOURCE 一次新的請(qǐng)求任務(wù) SourceGenerator

因?yàn)檫@里我們沒有配置緩存蘑拯,那么直接看 SourceGenerator

@Override
  public boolean startNext() {
        ...
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //獲取一個(gè) ModelLoad 加載器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //使用加載器中的 fetcher 根據(jù)優(yōu)先級(jí)加載數(shù)據(jù)
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

代碼邏輯很簡單,首先獲取加載器兜粘,然后從加載器中使用fetcher來加載數(shù)據(jù)就可以了强胰。

 List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear(); 
      //從 Glide 注冊(cè)的 Model 來獲取加載器(注冊(cè)是在 Glide 初始化的時(shí)候通過 registry
       // .append()添加的)
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
     
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current =
          // 構(gòu)建加載器。modelLoader是一個(gè)接口妹沙,需要知道是那個(gè)類的buildLoadData()來構(gòu)建加載器偶洋。看看下面的分析距糖。
            modelLoader.buildLoadData(model, width, height, options);
        //如果加載器器不為空玄窝,那么添加進(jìn)臨時(shí)緩存
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

獲取到加載器之后,會(huì)通過加載器中的fetcher加載數(shù)據(jù)悍引,然后我們進(jìn)入loadData()一看恩脂,fetcher是一個(gè)接口(DataFetcher<Data>),實(shí)現(xiàn)類有一大堆(如下圖)趣斤,我們根本沒法知道到底是哪個(gè)來加載數(shù)據(jù)俩块。

fetcher實(shí)現(xiàn)類.png

此處就是框架設(shè)計(jì)巧妙的一點(diǎn)了,到底是怎么知道哪個(gè)fetcher呢?

此時(shí)我們要回到 Glide 初始的時(shí)候 通過 Registry.append() 添加的S窨J迫!B汀I诱! 下面我們來看看盲厌。

Glide(
      @NonNull Context context,
      @NonNull Engine engine,
      @NonNull MemoryCache memoryCache,
      @NonNull BitmapPool bitmapPool,
      @NonNull ArrayPool arrayPool,
      @NonNull RequestManagerRetriever requestManagerRetriever,
      @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      @NonNull RequestOptionsFactory defaultRequestOptionsFactory,
      @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
      @NonNull List<RequestListener<Object>> defaultRequestListeners,
      boolean isLoggingRequestOriginsEnabled,
      boolean isImageDecoderEnabledForBitmaps) {
    ......
    registry
        .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))
        // 我們load傳入的是一個(gè)url署照,所以我們的fetcher就是HttpGlideUrlLoader。
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
        .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
        .append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
        .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
        .append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
        /* Transcoders */
        .register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
        .register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
        .register(
            Drawable.class,
            byte[].class,
            new DrawableBytesTranscoder(
                bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
    
    ......
}

從Glide的構(gòu)造方法中吗浩,我們找到了線索建芙,registry.append()會(huì)根據(jù)我們load()傳入不同類型的參數(shù),為我們注冊(cè)不同的fetcher懂扼。

// 說明:load傳入?yún)?shù)類型----加載后的資源形式--------加載器----
append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

我們可以這么理解岁钓,我們傳入的參數(shù)是URL,使用HttpGlideUrlLoader來加載微王,最終返回一個(gè)InputStream流屡限。看了注冊(cè)加載器之后炕倘,我們繼續(xù)回到如何構(gòu)建加載器的主路線上:

// LoadData<InputStream>就是加載器包裹钧大,HttpUrlFetcher就是我們要找到真正的網(wǎng)絡(luò)資源加載器!U中0⊙搿!
  @Override
  public LoadData<InputStream> buildLoadData(
      @NonNull GlideUrl model, int width, int height, @NonNull 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);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

此時(shí)就很清晰了涨醋,我們是通過HttpUrlFetcher來加載資源的瓜饥,然后我們進(jìn)入資源加載環(huán)節(jié)。

4.4 加載資源

從上一小節(jié)中浴骂,我們知道了我們傳入的url是通過HttpUrlFetcher來加載資源乓土。那么我們就進(jìn)入HttpUrlFetcher的loadData()。

加載資源.png
@Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      //http 請(qǐng)求溯警,返回一個(gè) InputStream 輸入流
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      //將 InputStream 以回調(diào)形式回調(diào)出去
      callback.onDataReady(result);
    } catch (IOException e) {
      callback.onLoadFailed(e);
    } finally {
     ...
    }
  }
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 {

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

    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();

    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    } 
    ...//拋的異常我們暫時(shí)先不管
  }

從上面代碼我們可以看出 HttpURLConnection 作為 Glide 底層成網(wǎng)絡(luò)請(qǐng)求的趣苏。請(qǐng)求成功之后直接返回的是一個(gè)輸入流,最后會(huì)通過 onDataReady 回調(diào)到 DecodeJob的onDataFetcherReady 函數(shù)中梯轻。

此時(shí)我們已經(jīng)從網(wǎng)絡(luò)上獲取到資源了食磕,現(xiàn)在來看是如何回調(diào)的。

@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

這里會(huì)有 else 因?yàn)槲覀儧]有配置緩存,繼續(xù)回調(diào):

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      ...
      @Override
      public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
          DataSource dataSource, Key attemptedKey) {
        this.currentSourceKey = sourceKey; //當(dāng)前返回?cái)?shù)據(jù)的 key
        this.currentData = data; //返回的數(shù)據(jù)
        this.currentFetcher = fetcher; //返回的數(shù)據(jù)執(zhí)行器喳挑,這里可以理解為 HttpUrlFetcher
        this.currentDataSource = dataSource; //數(shù)據(jù)來源 url
        this.currentAttemptingKey = attemptedKey;
        if (Thread.currentThread() != currentThread) {
          runReason = RunReason.DECODE_DATA;
          callback.reschedule(this);
        } else {
          GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
          try {
            //解析返回回來的數(shù)據(jù)
            decodeFromRetrievedData();
          } finally {
            GlideTrace.endSection();
          }
        }
      }   
      ...
    }

至此彬伦,我們的數(shù)據(jù)已經(jīng)從網(wǎng)絡(luò)上加載完成滔悉。最后通過decodeFromRetrievedData()方法來解析inputstream流。

4.5 解析資源

從上一小節(jié)单绑,我們拿到了資源流回官,下面我們來看看是如何解析資源的。

解析資源.png
  //解析返回的數(shù)據(jù)
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 調(diào)用 decodeFrom 解析 數(shù)據(jù)询张;HttpUrlFetcher , InputStream ,  currentDataSource
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    //解析完成后孙乖,通知下去
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
      DataSource dataSource) throws GlideException {
      ...  
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ....
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

  @SuppressWarnings("unchecked")
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    //獲取當(dāng)前數(shù)據(jù)類的解析器 LoadPath 
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    //通過 LoadPath 解析器來解析數(shù)據(jù)
    return runLoadPath(data, dataSource, path);
  }

  private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
      LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    
    //因?yàn)檫@里返回的是一個(gè) InputStream 所以 這里拿到的是 InputStreamRewinder
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      //將解析資源的任務(wù)轉(zhuǎn)移到 Load.path 方法中
      return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }

注意上面代碼浙炼,為了解析數(shù)據(jù)首先構(gòu)建一個(gè) LoadPath, 然后創(chuàng)建一個(gè) InputStreamRewinder 類型的 DataRewinder, 最終將數(shù)據(jù)解析的操作放到了 LoadPath.load 方法中 份氧,接下來看下 LoadPath.load 方法的具體邏輯操作:

public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
      int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
      
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      listPool.release(throwables);
    }
  }

  private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
      List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
   
    //遍歷內(nèi)部存儲(chǔ)的 DecodePath 集合,通過他們來解析數(shù)據(jù)
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        //這里才是真正解析數(shù)據(jù)的地方
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
       ...
      }
     ...
    return result;
  }

最終通過path.decode()根據(jù)尺寸來解析inputstream流弯屈。

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //解析完數(shù)據(jù)回調(diào)出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //轉(zhuǎn)換資源為目標(biāo)資源
    return transcoder.transcode(transformed, options);
  }

看看 decodeResource 怎么解析成中間資源的:

@NonNull
  private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
   ...
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
    ...
    }
  }

  @NonNull
  private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
    Resource<ResourceType> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          // 調(diào)用 ResourceDecoder.decode 解析數(shù)據(jù)
          result = decoder.decode(data, width, height, options);
        }

      } catch (IOException | RuntimeException | OutOfMemoryError e) {
                ...
      }
    return result;
  }

可以看到數(shù)據(jù)解析的任務(wù)最終是通過 DecodePath 來執(zhí)行的, 它內(nèi)部有三大步操作

  • 第一大步:deResource 將源數(shù)據(jù)解析成資源(源數(shù)據(jù): InputStream蜗帜, 中間產(chǎn)物: Bitmap)
  • 第二大步:調(diào)用 DecodeCallback.onResourceDecoded 處理資源
  • 第三大步:調(diào)用 ResourceTranscoder.transcode 將資源轉(zhuǎn)為目標(biāo)資源(目標(biāo)資源類型: Drawable)

可以發(fā)現(xiàn),通過上面的 decoder.decode 源碼可知资厉,它是一個(gè)接口厅缺,由于我們這里的源數(shù)據(jù)是 InputStream,所以,它的實(shí)現(xiàn)類是 StreamBitmapDecoder類 ,我們就來看下 它內(nèi)部的解碼過程:

@Override
  public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
      @NonNull Options options)
      throws IOException {

    // Use to fix the mark limit to avoid allocating buffers that fit entire images.
    final RecyclableBufferedInputStream bufferedStream;
    final boolean ownsBufferedStream;

    ....
      
    try {
      // 根據(jù)請(qǐng)求配置來對(duì)數(shù)據(jù)進(jìn)行采樣壓縮宴偿,獲取一個(gè) Resource<Bitmap> 
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      ....
    }
  }

最終是通過downsampler去根據(jù)目標(biāo)的寬高湘捎,來對(duì)輸入數(shù)據(jù)流進(jìn)行壓縮。

回到主線中的三大步:

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //第一步: 調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源 Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //第二步: 解析完數(shù)據(jù)回調(diào)出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //第三步: 轉(zhuǎn)換資源為目標(biāo)資源 Bitmap to Drawable
    return transcoder.transcode(transformed, options);
  }

解析完數(shù)據(jù)窄刘,看第二注釋里面回調(diào)窥妇,最后會(huì)回調(diào)到 DecodeJob:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      ...  
    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    } 
      ... 
}
@Synthetic
  @NonNull
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
    @SuppressWarnings("unchecked")
    //獲取資源類型
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    //如果不是從磁盤資源中獲取需要進(jìn)行 transform 操作
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    ...
    //構(gòu)建數(shù)據(jù)編碼的策略
    final EncodeStrategy encodeStrategy;
    final ResourceEncoder<Z> encoder;
    if (decodeHelper.isResourceEncoderAvailable(transformed)) {
      encoder = decodeHelper.getResultEncoder(transformed);
      encodeStrategy = encoder.getEncodeStrategy(options);
    } else {
      encoder = null;
      encodeStrategy = EncodeStrategy.NONE;
    }
        
    //根據(jù)編碼策略,構(gòu)建緩存 Key
    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      if (encoder == null) {
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
      }
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          //源數(shù)據(jù) key
          key = new DataCacheKey(currentSourceKey, signature);
          break;
          //... 省略 成噸的代碼
      }
      //初始化編碼管理者娩践,用于提交內(nèi)存緩存
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    //返回轉(zhuǎn)換后的 Bitmap
    return result;
  }

此處說白了活翩, onResourceDecoded 中, 主要是對(duì)中間資源做了如下的操作:

第一步:對(duì)資源進(jìn)行了轉(zhuǎn)換操作。比如 Fit_Center,CenterCrop, 這些都是在請(qǐng)求的時(shí)候配置的翻伺;

第二步:構(gòu)建磁盤緩存的 key材泄。

資源解析完成,已經(jīng)存入磁盤吨岭,此處開始對(duì)資源繼續(xù)轉(zhuǎn)換拉宗。

4.6 資源轉(zhuǎn)換

從上一節(jié)我們通過解碼inputstream流得到Bitmap,然而我們顯示需要將Bitmap轉(zhuǎn)化成Drawable辣辫。下面我們繼續(xù)接著上一小節(jié)看看資源是如何轉(zhuǎn)換的簿废。

資源轉(zhuǎn)換.png
public class DecodePath<DataType, ResourceType, Transcode> { 
  //省略成噸的代碼 ...  
  Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //第一步: 調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源 Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //第二步: 解析完數(shù)據(jù)回調(diào)出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //第三步: 轉(zhuǎn)換資源為目標(biāo)資源 Bitmap to Drawable
    return transcoder.transcode(transformed, options);
  }
  // 省略成噸的代碼 ... 
}

我們繼續(xù)看第三步。通過源碼可知络它,ResourceTranscoder 是一個(gè)接口族檬,又因?yàn)榻馕鐾甑臄?shù)據(jù)是 Bitmap 所以它的實(shí)現(xiàn)類是 BitmapDrawableTranscoder ,最后看下它的 transcode 具體實(shí)現(xiàn):

public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
  @Nullable
  @Override
  public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
      @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  } 
}
public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
    Initializable {

  private final Resources resources;
  private final Resource<Bitmap> bitmapResource;

  @Deprecated
  public static LazyBitmapDrawableResource obtain(Context context, Bitmap bitmap) {
    return
        (LazyBitmapDrawableResource)
            obtain(
                context.getResources(),
                BitmapResource.obtain(bitmap, Glide.get(context).getBitmapPool()));
  }

  @Deprecated
  public static LazyBitmapDrawableResource obtain(Resources resources, BitmapPool bitmapPool,Bitmap bitmap) {
    return (LazyBitmapDrawableResource) obtain(resources, BitmapResource.obtain(bitmap, bitmapPool));
  }

  @Nullable
  public static Resource<BitmapDrawable> obtain(
      @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }

  private LazyBitmapDrawableResource(@NonNull Resources resources,
      @NonNull Resource<Bitmap> bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

  @NonNull
  @Override
  public Class<BitmapDrawable> getResourceClass() {
    return BitmapDrawable.class;
  }

  // Get 方法返回了一個(gè) BitmapDrawable 對(duì)象
  @NonNull
  @Override
  public BitmapDrawable get() {
    return new BitmapDrawable(resources, bitmapResource.get());
  }

  @Override
  public int getSize() {
    return bitmapResource.getSize();
  }

  @Override
  public void recycle() {
    bitmapResource.recycle();
  }

  @Override
  public void initialize() {
    if (bitmapResource instanceof Initializable) {
      ((Initializable) bitmapResource).initialize();
    }
  }
}

轉(zhuǎn)化終于完成了 化戳,將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內(nèi)部, 然后外界通過 get 方法就可以獲取到一個(gè) BitmapDrawable 的對(duì)象了单料,解析完就到了展示數(shù)據(jù)了埋凯,請(qǐng)看下面代碼:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  //解析返回的數(shù)據(jù)
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      //第一步: 調(diào)用 decodeFrom 解析 數(shù)據(jù);HttpUrlFetcher , InputStream ,  currentDataSource
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    //第二步: 解析完成后扫尖,通知下去
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }  
 }

第一步就解析完了數(shù)據(jù)白对, 現(xiàn)在第二步執(zhí)行 notifyEncodeAndRelease函數(shù):

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    ...
    //通知調(diào)用層數(shù)據(jù)已經(jīng)裝備好了
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      //這里就是將資源磁盤緩存
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
     ...
    }
    //完成
    onEncodeComplete();
  }

  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    // 在 DecodeJob 的構(gòu)建中, 我們知道這個(gè) Callback 是 EngineJob
    callback.onResourceReady(resource, dataSource);
  }
}

可以看到上面的 DecodeJob.decodeFromRetrievedData 中主要做了三個(gè)處理:

第一個(gè)處理:解析返回回來的資源。

第二個(gè)處理:拿到解析的資源换怖,如果配置了本地緩存甩恼,就緩存到磁盤。

第三個(gè)處理:通知上層資源準(zhǔn)備就緒沉颂,可以使用了条摸。

我們直接看 EngineJob 的 onResourceReady 回調(diào)函數(shù):

 @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }

  @Synthetic
  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
      ... 
      }
      engineResource = engineResourceFactory.build(resource, isCacheable);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }

    //回調(diào)上層 Engine 任務(wù)完成了
    listener.onEngineJobComplete(this, localKey, localResource);

    //遍歷資源回調(diào)給 ImageViewTarget 
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }

通過上面 EngineJob 的 onResourceReady 回調(diào)函數(shù) 主要做了 兩個(gè)處理:

第一個(gè)處理:通知上層任務(wù)完成。 

第二個(gè)處理:回調(diào) ImageViewTarget 用于展示數(shù)據(jù)铸屉。

看下 listener.onEngineJobComplete 具體實(shí)現(xiàn):

@SuppressWarnings("unchecked")
  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null) {
      resource.setResourceListener(key, this);
            //收到下游返回回來的資源钉蒲,添加到活動(dòng)緩存中
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    jobs.removeIfCurrent(key, engineJob);
  }

最終通知 ImageViewTarget, 看下具體操作:

//遍歷資源回調(diào)給 ImageViewTarget 
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
 private class CallResourceReady implements Runnable {

    private final ResourceCallback cb;

    CallResourceReady(ResourceCallback cb) {
      this.cb = cb;
    }

    @Override
    public void run() {
      synchronized (EngineJob.this) {
        if (cbs.contains(cb)) {
         ...
          //返回準(zhǔn)備好的資源
          callCallbackOnResourceReady(cb);
          removeCallback(cb);
        }
        decrementPendingCallbacks();
      }
    }
  }

我們可以看到 CallResourceReady 實(shí)現(xiàn) Runnable ,當(dāng) entry.executor.execute 線程池執(zhí)行的時(shí)候就會(huì)調(diào)用 run 彻坛,最后我們繼續(xù)跟 callCallbackOnResourceReady函數(shù):

  @Synthetic
  synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      //回調(diào)給 SingleRequest
      cb.onResourceReady(engineResource, dataSource);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }

SingleRequest onResourceReady 回調(diào)實(shí)現(xiàn):

public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    loadStatus = null;
    ... 省略成噸的代碼
    Object received = resource.get();
    if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
      releaseResource(resource);
      ... 省略成噸的代碼
      onLoadFailed(exception);
      return;
    }

    if (!canSetResource()) {
      releaseResource(resource);
      status = Status.COMPLETE;
      return;
    }

   //當(dāng)資源準(zhǔn)備好的時(shí)候
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }

private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
      ... 省略成噸的代碼
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        //回調(diào)給目標(biāo) ImageViewTarget 資源準(zhǔn)備好了
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }
    //加載成功
    notifyLoadSuccess();
  }

這一步主要把準(zhǔn)備好的資源回調(diào)給顯示層!!!!!!! 終于快看到勝利的曙光了顷啼。

4.7 顯示資源

資源終于完全準(zhǔn)備好了,下面就是加載資源到控件上昌屉。

加載資源到控件上.png
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter { 
  ...
  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
 
  protected abstract void setResource(@Nullable Z resource);
  ...
}

  private void setResourceInternal(@Nullable Z resource) {
    //調(diào)用 setResource 函數(shù)钙蒙,將資源顯示出來
    setResource(resource);
    ...
  }

大家還記得么?在最開始構(gòu)建的時(shí)候间驮,我們知道只有調(diào)用 asBitmap 的時(shí)候?qū)崿F(xiàn)類是 BitmapImageViewTarget,在這里的測(cè)試躬厌,并沒有調(diào)用這個(gè)函數(shù),所以它的實(shí)現(xiàn)類是 DrawableImageViewTarget,具體看下它內(nèi)部實(shí)現(xiàn):

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

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

  // Public API.
  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
}

這里看到抽象類中調(diào)用了 setResource ,子類實(shí)現(xiàn)并調(diào)用了 view.setImageDrawable(resource); 圖片現(xiàn)在算是真正的顯示出來了蜻牢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末烤咧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抢呆,更是在濱河造成了極大的恐慌煮嫌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抱虐,死亡現(xiàn)場離奇詭異昌阿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恳邀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門懦冰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谣沸,你說我怎么就攤上這事刷钢。” “怎么了乳附?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵内地,是天一觀的道長伴澄。 經(jīng)常有香客問我,道長阱缓,這世上最難降的妖魔是什么非凌? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任祸憋,我火速辦了婚禮桃漾,結(jié)果婚禮上豪筝,老公的妹妹穿的比我還像新娘决左。我一直安慰自己,他們只是感情好庞萍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布焰宣。 她就那樣靜靜地躺著榆芦,像睡著了一般沃粗。 火紅的嫁衣襯著肌膚如雪粥惧。 梳的紋絲不亂的頭發(fā)上键畴,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天最盅,我揣著相機(jī)與錄音,去河邊找鬼起惕。 笑死涡贱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惹想。 我是一名探鬼主播问词,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼嘀粱!你這毒婦竟也來了激挪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤锋叨,失蹤者是張志新(化名)和其女友劉穎垄分,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娃磺,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡薄湿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偷卧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豺瘤。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖听诸,靈堂內(nèi)的尸體忽然破棺而出坐求,到底是詐尸還是另有隱情,我是刑警寧澤晌梨,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布桥嗤,位于F島的核電站赛糟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏砸逊。R本人自食惡果不足惜璧南,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望师逸。 院中可真熱鬧司倚,春花似錦、人聲如沸篓像。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽员辩。三九已至盒粮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奠滑,已是汗流浹背丹皱。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宋税,地道東北人摊崭。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像杰赛,于是被迫代替她去往敵國和親呢簸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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