[Glide4源碼解析系列] — 2.Glide數(shù)據(jù)模型轉(zhuǎn)換與數(shù)據(jù)抓取

Glide

Glide4源碼解析系列

[Glide4源碼解析系列]--1.Glide初始化
[Glide4源碼解析系列]--2.Glide數(shù)據(jù)模型轉(zhuǎn)換與數(shù)據(jù)抓取
[Glide4源碼解析系列]--3.Glide數(shù)據(jù)解碼與轉(zhuǎn)碼


一、簡介

上一篇文章,我們梳理了一遍Glide的初始化流程,看到了Gilde在Glide#with一句簡單的代碼背后做了巨大的準(zhǔn)備工作,而這所有的準(zhǔn)備工作,都是為了接下來順利地完成數(shù)據(jù)解析和顯示做了鋪墊。

在上一遍文章中提到,Glide的數(shù)據(jù)解碼流程可以分為以下幾個步驟(如果沒看上一篇文章,建議可以先看看):

model(數(shù)據(jù)源)-->data(轉(zhuǎn)換數(shù)據(jù))-->decode(解碼)-->transformed(縮放)-->transcoded(轉(zhuǎn)碼)-->encoded(編碼保存到本地)

那么本篇文章就重點來看看Glide的數(shù)據(jù)轉(zhuǎn)換與數(shù)據(jù)抓取流程。

我們依然從一句代碼開始,那就是RequestManager#load

二、Glide資源加載前的準(zhǔn)備

1. RequestManager.load裝載原始數(shù)據(jù)

通過上一篇文章,我們知道,Glide.with()最后給我們返回了一個請求管理工具,那就是RequestManger。通常如果是簡單的加載一張圖片的話,我們調(diào)用鏈如下:


Glide.with(this).load(url).into(iv_img);

在RequestManger的中,對load方法進(jìn)行了多個類型的重載,基本上可以滿足日常圖片加載類型,如String, URL,Bitmap,InputStream,F(xiàn)ile等等。

為了方便分析和流程的梳理,我們需要指定一個數(shù)據(jù)類型來進(jìn)行跟蹤,其它的流程基本是一致的,只是過程中使用的轉(zhuǎn)換模型和解碼方式不一樣而已。

那么,這里就使用日常最常使用的,加載一個網(wǎng)絡(luò)圖片作為分析源頭。我們直接進(jìn)入:

  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
  
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  
  public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

首先看到,在最后這個as方法中,創(chuàng)建了一個RequestBuilder,即一個請求的構(gòu)建者,用來構(gòu)建個Request請求工具。

這里需要注意一個參數(shù),即as(Drawable.class),這個Drawable.class類型,將是最后轉(zhuǎn)碼得到的,最終用于顯示的數(shù)據(jù)類型。

其次,asDrawable()得到一個RequestBuilder,然后通過其load方法,將原始數(shù)據(jù)設(shè)置給RequstBuilder。

那么,我們就來看看RequestBuild做了什么。

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

可以看到,這里并沒有馬上進(jìn)入數(shù)據(jù)請求加載過程,而是簡單的將數(shù)據(jù)模式進(jìn)行了保存,并將isModelSet設(shè)置為true,然后返回。

那么什么時候才開始進(jìn)入數(shù)據(jù)加載流程。那就要來看看RequestBuilder#into()方法了。

2. RequestBuilder.into()啟動資源加載
1)生成請求參數(shù),及顯示目標(biāo)
public ViewTarget<ImageView, TranscodeType> into(ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

根據(jù)ImageView設(shè)置的縮放類型,配置一個請求參數(shù),這里設(shè)置的縮放工具,就是加載流程中transformed(縮放)使用到的工具。

重點看最后一個調(diào)用,第一個參數(shù)

glideContext.buildImageViewTarget(view, transcodeClass),

這里會根據(jù)transcodeClass類型生成一個ViewTarget,這里transcodeClass為Drawable.class,所有將會生成一個DrawableImageViewTarget。

2)構(gòu)建請求,并啟動數(shù)據(jù)加載

進(jìn)入into方法中,過程比較簡單,直接看代碼中的注釋:

private <Y extends Target<TranscodeType>> Y into(
  @NonNull Y target,
  @Nullable RequestListener<TranscodeType> targetListener,
  RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    //第一:通過isModelSet檢查是否通過load設(shè)置了數(shù)據(jù)源,否則拋出異常;
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    
    options = options.autoClone();
    
    //第二:創(chuàng)建請求;
    Request request = buildRequest(target, targetListener, options);
    
    //第三:判斷當(dāng)前請求是否已經(jīng)存在,
    //是的話,直接啟動請求;
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and untracking Targets, and obtaining View dimensions that
        // are done in the individual Request.
        previous.begin();
      }
      return target;
    }
    
    //第四:保存當(dāng)前請求到ViewTarget的Tag中,
    //并將Request添加RequestManager中進(jìn)行跟蹤維護(hù)。
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);
    
    return target;
}

一看便知,重點在第二和第四兩步上。

首先來看下第二步,構(gòu)建一個Request:

private Request buildRequest(
  Target<TranscodeType> target,
  @Nullable RequestListener<TranscodeType> targetListener,
  RequestOptions requestOptions) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions);
}

private Request buildRequestRecursive(
  Target<TranscodeType> target,
  @Nullable RequestListener<TranscodeType> targetListener,
  @Nullable RequestCoordinator parentCoordinator,
  TransitionOptions<?, ? super TranscodeType> transitionOptions,
  Priority priority,
  int overrideWidth,
  int overrideHeight,
  RequestOptions requestOptions) {

    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
    
    //構(gòu)建目標(biāo)請求
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
    
    int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
    int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
    if (Util.isValidDimensions(overrideWidth, overrideHeight)
        && !errorBuilder.requestOptions.isValidOverride()) {
      errorOverrideWidth = requestOptions.getOverrideWidth();
      errorOverrideHeight = requestOptions.getOverrideHeight();
    }
    
    Request errorRequest = errorBuilder.buildRequestRecursive(
        target,
        targetListener,
        errorRequestCoordinator,
        errorBuilder.transitionOptions,
        errorBuilder.requestOptions.getPriority(),
        errorOverrideWidth,
        errorOverrideHeight,
        errorBuilder.requestOptions);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
}

直接看第二個方法buildRequestRecursive,遞歸構(gòu)建請求。從方法命名來看,請求不一定只有一個,而是會視情況遞歸地去構(gòu)建多個請求,這些請求類型包括:

  • 錯誤圖片請求(正常的請求出錯時,如果有配置該請求,則啟動該請求)
  • 縮略圖請求(小圖請求,可以較快顯示。如有配置,在請求開始時,就會啟動)
  • 目標(biāo)圖片請求(目標(biāo)圖片請求,在請求開始時,就是啟動)

如果除了目標(biāo)圖片外,用戶還配置了錯誤圖片顯示,或縮略圖顯示,那么,這時候會創(chuàng)建一個請求協(xié)調(diào)器,來協(xié)調(diào)各類型圖片間的請求順序。

在看協(xié)調(diào)器之前,我們先來看下Request這類,它是一個接口類,規(guī)定了請求相關(guān)的接口,如開始/停止/清除/回收請求......

public interface Request {
  void begin();
  void pause();
  void clear();
  boolean isPaused();
  boolean isRunning();
  boolean isComplete();
  boolean isResourceSet();
  boolean isCancelled();
  boolean isFailed();
  void recycle();
  boolean isEquivalentTo(Request other);
}

而協(xié)調(diào)器,其實也是一個Request的實現(xiàn)類,比如上面第二個方法中的ErrorRequestCoordinator

public final class ErrorRequestCoordinator implements RequestCoordinator,
    Request {

  @Nullable
  private final RequestCoordinator parent;
  private Request primary;
  private Request error;

  public ErrorRequestCoordinator(@Nullable RequestCoordinator parent) {
    this.parent = parent;
  }

  public void setRequests(Request primary, Request error) {
    this.primary = primary;
    this.error = error;
  }

  @Override
  public void begin() {
    if (!primary.isRunning()) {
      primary.begin();
    }
  }

  @Override
  public void pause() {
    if (!primary.isFailed()) {
      primary.pause();
    }
    if (error.isRunning()) {
      error.pause();
    }
  }

  @Override
  public void clear() {
    primary.clear();
    if (primary.isFailed()) {
      error.clear();
    }
  }
  
  @Override
  public void onRequestFailed(Request request) {
    if (!request.equals(error)) {
      if (!error.isRunning()) {
        error.begin();
      }
      return;
    }

    if (parent != null) {
      parent.onRequestFailed(this);
    }
  }
  
  //省略其余方法......
}
  1. 在構(gòu)建協(xié)調(diào)器后,會將目標(biāo)圖片請求和錯誤圖片請求設(shè)置給協(xié)調(diào)器。
  2. 一旦請求begin,就會啟動目標(biāo)圖片請求。
  3. 當(dāng)目標(biāo)圖片請求失敗時,就會啟動錯誤圖片請求。

其它的協(xié)調(diào)器也是類似的,只不過各類型請求啟動的時機(jī)不一樣罷了!

接著,我們回到剛剛buildRequestRecursive方法中,為了方便分析,我們簡化一下流程,只看請求只有目標(biāo)圖片的情況。

構(gòu)建目標(biāo)圖片用是在buildThumbnailRequestRecursive方法中:

private Request buildThumbnailRequestRecursive(
  Target<TranscodeType> target,
  RequestListener<TranscodeType> targetListener,
  @Nullable RequestCoordinator parentCoordinator,
  TransitionOptions<?, ? super TranscodeType> transitionOptions,
  Priority priority,
  int overrideWidth,
  int overrideHeight,
  RequestOptions requestOptions) {
    if (thumbnailBuilder != null) {
        //構(gòu)建請求......
    } else if (thumbSizeMultiplier != null) {
        //構(gòu)建請求......
    } else {
      // Base case: no thumbnail.
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
    }
}

看最后一個else,只有目標(biāo)圖片的情況,這里會構(gòu)建并返回一個Request:

private Request obtainRequest(
  Target<TranscodeType> target,
  RequestListener<TranscodeType> targetListener,
  RequestOptions requestOptions,
  RequestCoordinator requestCoordinator,
  TransitionOptions<?, ? super TranscodeType> transitionOptions,
  Priority priority,
  int overrideWidth,
  int overrideHeight) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListener,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory());
}

通過SingleRequest.obtain獲取到了一個SingleRequst的單例(這一堆的參數(shù)讓人忍不住想吐槽一下)。

到這構(gòu)建好了Request,那么接下來就是啟動請求了。

3)啟動請求

回到into方法中:


private <Y extends Target<TranscodeType>> Y into(
  @NonNull Y target,
  @Nullable RequestListener<TranscodeType> targetListener,
  RequestOptions options) {
  
    //省略部分代碼......
    
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);
    
    return target;
}

在平時使用Glide過程中,我們可能會調(diào)用ImageView的setTag來緩存一些數(shù)據(jù),但是在使用Glide加載圖片的時候,就會拋出異常,告訴我們使用Glide來加載圖片的ImageView不能調(diào)用setTag方法,這是為什么呢?原因就在這句代碼:

target.setRequest(request);

這句代碼會將Request緩存到ImageView的tag中,如果你確實需要緩存數(shù)據(jù),那么你只能調(diào)用setTag(int key, Object tag)給tag設(shè)置一個key。

最后,將這個請求放到RequestManager的請求隊列中,同時發(fā)起加載請求。

//RequestManager.java
void track(Target<?> target, Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
}

//RequestTracker.java
public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
}

那么,Glide就通過RequestManager、RequestOption、Request,構(gòu)建了一個請求序列,并通過監(jiān)聽生命周期來動態(tài)管理Request的開啟、暫停、恢復(fù)、銷毀等。

三、開啟資源加載任務(wù)

來到SingleRequest#begin方法中(非完整代碼,省略一些不太緊要的代碼)

public void begin() {

    //省略部分代碼...
    
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }
    
    //重點在以下if中
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }
    
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
}

public void onSizeReady(int width, int height) {
    
    //省略部分代碼...
    
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

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

    if (status != Status.RUNNING) {
      loadStatus = null;
    }
}

在begin方法中,如果圖片顯示尺寸有效,會直接調(diào)用onSizeReady。否則, 會調(diào)用target.getSize,去計算圖片尺寸,計算完畢后,同樣會回調(diào)onSizeReady方法。

因此,最終都會進(jìn)入到onSizeReady方法中,進(jìn)而調(diào)用Engine(請求加載引擎)的load方法。

代碼中簡單標(biāo)示了加載流程。

//Engine.java
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 useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();
    
    //1:創(chuàng)建資源索引key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
        
    //2:從內(nèi)存中當(dāng)前正在顯示的資源緩存加載圖片
    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;
    }
    
    //3:從內(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;
    }
    
    //4:獲取已經(jīng)存在的加載任務(wù)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }
    
    //5:新建加載任務(wù),用于啟動解碼任務(wù)
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    
    //6:新建解碼任務(wù),真正執(zhí)行數(shù)據(jù)加載和解碼的類
    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);
    
    //7:緩存加載任務(wù)
    jobs.put(key, engineJob);

    engineJob.addCallback(cb);
    
    //8:開啟解碼任務(wù)
    engineJob.start(decodeJob);
    
    return new LoadStatus(cb, engineJob);
}

以上共有8個步驟來獲取或開始一個資源的加載和解碼。分析一下:

1:創(chuàng)建資源索引key。
我們可以看到生成一個索引key需要資源本身、圖片寬高、轉(zhuǎn)換類型、加載參數(shù)等等,只要這些都一致的情況下,才判定為一個相同的圖片資源加載。所以,即便是要顯示的ImageView寬高不一樣,Glide都會重新執(zhí)行一次加載過程,而不是內(nèi)存中加載已有的圖片資源。

2和3:
如果要加載的圖片已經(jīng)正在顯示,直接使用已有的資源。如果圖片沒有在顯示,但是已經(jīng)正好還在內(nèi)存緩存中,沒有被銷毀,那么直接使用緩存中的資源

4到8:
如果內(nèi)存中并沒有可以直接使用的圖片資源,那么就要開始從網(wǎng)絡(luò)或者本地硬盤中去加載一張圖片。

還記得上一篇文中說到的,初始化過程中會創(chuàng)建幾個不同類的線程池,用于加載圖片資源嗎?Glide將每一個請求都封裝為一個解碼任務(wù)DecodeJob,并扔到線程池中,以此來開啟任務(wù)的異步加載。

public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
}

如此就很明了了,DecodeJob肯定是一個繼承Runnable的類,任務(wù)啟動的入口就在run方法中。

//DecodeJob.java

public void run() {

    //省略部分代碼...
    
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      //重點調(diào)用
      runWrapped();
    } catch (Throwable t) {
        
      //省略日志打印和注釋...
    
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      if (!isCancelled) {
        throw t;
      }
    } finally {
      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
      // close in all cases anyway.
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      TraceCompat.endSection();
    }
}

如果一切正常,那么會進(jìn)入runWrapped方法中

//DecodeJob.java

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

private Stage getNextStage(Stage current) {
    switch (current) {
      //初始狀態(tài):下一狀態(tài)為從處理過的資源緩存加載圖片
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      
      //狀態(tài)2:下一狀態(tài)從未處理過的原始資源加載圖片
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
            
      //狀態(tài)3:下一狀態(tài)從遠(yuǎn)程加載圖片
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      
      //狀態(tài)4:結(jié)束解碼
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}

private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        //處理過的緩存資源加載器
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        //原始圖片資源緩存加載器
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        //遠(yuǎn)程圖片資源加載器
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
}

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
    
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }
}

==這幾個方法構(gòu)成了Glide的解碼流程:==

  1. 嘗試從處理過的本地資源加載圖片
  2. 嘗試從未處理過的原始本地資源加載圖片
  3. 嘗試從遠(yuǎn)程加載圖片

因此,這是一個嵌套的循環(huán),通過狀態(tài)的切換來尋找下一個加載器,直到加載一張圖片,返回成功;或者找不到要加載的圖片,返回失敗。

==以上三個步驟分別對應(yīng)以下三個加載器:==

  1. ResourceCacheGenerator
  2. DataCacheGenerator
  3. SourceGenerator

接下來終于要進(jìn)入本文的重點部分了(鋪墊了一堆??,想講明白Glide真不容易啊~)

四、Glide數(shù)據(jù)模型轉(zhuǎn)換

1. 加載核心簡介

以上三個加載器是順序遍歷的,本文以加載一張網(wǎng)絡(luò)圖片來講解這個解碼過程,為了便于理解與理清加載邏輯,我們不按照這個流程來,而是從最后一個SourceGenerator加載器入手,因為,當(dāng)你第一次加載一張新的網(wǎng)絡(luò)圖片時,本地并沒有這張網(wǎng)絡(luò)圖片的緩存。

從runGenerators方法中看到,Generator的入口是startNext()方法

//SourceGenerator.java

public boolean startNext() {
    
    //1:判斷是否有緩存,如有,直接加載緩存
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

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

    //2:沒有緩存,從遠(yuǎn)程加載
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //3:獲取數(shù)據(jù)加載器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
}

分為兩種情況,第一中情況我們先不管,后面再詳細(xì)講到。直接來看第二種情況,也是首次加載的情況。

又是一個循環(huán)遍歷,遍歷所有的ModelLoader模塊加載器。為了更好的理解,我們先來了解下Glide用來數(shù)據(jù)加載和解碼的幾個模塊。

類名 作用
1. SourceGenerator繼承DataFetcherGenerator DataFecher生成器
2. ModelLoader 數(shù)據(jù)轉(zhuǎn)換和創(chuàng)建LoadData
3. LoadData 數(shù)據(jù)加載(包含DataFecher)
4. DataFecher 數(shù)據(jù)抓取
5. LoadPath 加載器包含多個DecodePath
6. DecodePath 解碼器

從命名上也可以看出他們之間的些許聯(lián)系:

  • Generator是一個DataFecher生成器(還有ResourceCacheGenerator和DataCacheGenerator)

  • DataFether是一個數(shù)據(jù)抓取器,存放在LoadData中。
    而這些LoadData是從哪里生產(chǎn)的呢?就是ModelLoader。

    以上1-4構(gòu)成了Glide數(shù)據(jù)轉(zhuǎn)換與獲取(如:String --> url --> InputStream)的核心;
    5-6則構(gòu)成Glide數(shù)據(jù)解碼的核心(5-6我們在下一篇文章再詳細(xì)分析)。

  • 當(dāng)抓取到數(shù)據(jù)以后,需要對數(shù)據(jù)進(jìn)行解碼,這時候就會用到DecodePath來進(jìn)行解碼,而DecodePath正是存放在LoadPath當(dāng)中的。

這幾個工具,基本構(gòu)成了Glide數(shù)據(jù)加載過程的核心。

2. 模型轉(zhuǎn)換匹配
1)數(shù)據(jù)轉(zhuǎn)換,獲取ModelLoader

Glide是如何后獲取到匹配的模型加載器的?看startNext的第3步,進(jìn)入helper.getLoadData(),其中helper為DecoderHelper,解碼任務(wù)的幫助類。

//DecoderHelper.java

List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      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);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
}

看到熟悉的代碼了嗎?

List<ModelLoader<Object, ?>> modelLoaders =
glideContext.getRegistry().getModelLoaders(model);

這里獲取的不就是Glide在初始化的時候那個注冊器嗎?而getModelLoaders獲取的就是model類型對應(yīng)的數(shù)據(jù)轉(zhuǎn)換器ModelLoader(如果不了解,建議先看上篇文章)。

下面只看關(guān)鍵的方法調(diào)用,

首先是ModelLoaderRegistry.java

  //ModelLoaderRegistry.java
  
  public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(A model) {
    //獲取model類型對應(yīng)的數(shù)據(jù)模型加載器
    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;
  }

  private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
    List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
    if (loaders == null) {
      //調(diào)用工廠方法,構(gòu)建ModelLoader
      loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
      cache.put(modelClass, loaders);
    }
    return loaders;
  }

相信大家都看得懂,關(guān)鍵代碼就是multiModelLoaderFactory.build(modelClass)

  //MultiModelLoaderFactory.java

  synchronized <Model> List<ModelLoader<Model, ?>> build(Class<Model> modelClass) {
    try {
      List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
      //1:遍歷所有注冊的模型轉(zhuǎn)換器
      for (Entry<?, ?> entry : entries) {
        //2:過濾重復(fù)
        if (alreadyUsedEntries.contains(entry)) {
          continue;
        }
        //3:如果是要轉(zhuǎn)換的數(shù)據(jù)類型
        if (entry.handles(modelClass)) {
          alreadyUsedEntries.add(entry);
          //4:添加構(gòu)建好的ModelLoader
          loaders.add(this.<Model, Object>build(entry));
          alreadyUsedEntries.remove(entry);
        }
      }
      return loaders;
    } catch (Throwable t) {
      alreadyUsedEntries.clear();
      throw t;
    }
  }
  
  private <Model, Data> ModelLoader<Model, Data> build(Entry<?, ?> entry) {
    //調(diào)用工廠方法構(gòu)建ModelLoader
    return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
  }
  
  //判斷數(shù)據(jù)類型是否為要查找的數(shù)據(jù)類型,或者父類
  //即this.modelClass == modelClass 或者 modelClass為this.modelClass的子類
  public boolean handles(Class<?> modelClass) {
      return this.modelClass.isAssignableFrom(modelClass);
  }

那么,先把String對應(yīng)的類型轉(zhuǎn)換以及相應(yīng)的工廠列出來

Model 轉(zhuǎn)換類型 ModelLoader工廠
String.class InputStream.class DataUrlLoader.StreamFactory
String.class InputStream.class StringLoader.StreamFactory
String.class InputStream.class StringLoader.FileDescriptorFactory()

是不是覺得很簡單,就是三種轉(zhuǎn)換而已,事實并非如此,我們繼續(xù)往下看代碼。

 //DataUrlLoader.java

 private static final String DATA_SCHEME_IMAGE = "data:image";
 
 public boolean handles(String url) {
    return url.startsWith(DATA_SCHEME_IMAGE);
 }
 
 //DataUrlLoader#StreamFactory.java
 
 public final ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new DataUrlLoader<>(opener);
 }

從handles方法可以看出,DataUrlLoader是用來加載base64 Url圖片的。所以這會被過濾掉,不會用來處理普通的String類型圖片路徑。

剩下的兩個:

  //StringLoader.java
  
  public boolean handles(String model) {
    return true;
  }

  //StringLoader#StreamFactory.java
  
  public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
  }
  
  //StringLoader#FileDescriptorFactory.java
  
  public ModelLoader<String, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
      return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
    }

你會發(fā)現(xiàn),后面這兩個工廠什么鬼?是不是走錯片場了?通過multiFactory.build的方法又回到MultiModelLoaderFactory中了?

但是,如果你夠仔細(xì)的話,你又會發(fā)現(xiàn),此build非比build,這兒的build多了一個參數(shù)!?。?/p>

這就是Gilde數(shù)據(jù)模型轉(zhuǎn)換非常高明的地方了。這里不僅將String.class類型的數(shù)據(jù)轉(zhuǎn)換成了Uri.class的數(shù)據(jù),并且還精確縮小了搜索范圍,即要輸入為Uri,又要輸出只為InputStream.class和ParcelFileDescriptor.class的ModelLoader。

同時,也利用了Uri數(shù)據(jù)類型的ModelLoader的解析數(shù)據(jù)能力,來解析String類型的網(wǎng)絡(luò)圖片,不得不贊嘆Glide強(qiáng)大的架構(gòu)設(shè)計思維。

通過這種數(shù)據(jù)類型轉(zhuǎn)換的能力,Glide幾乎可以無縫的加載任意類型的圖片數(shù)據(jù)。

好了,我們繼續(xù)往下:

 public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass,
      Class<Data> dataClass) {
    try {
      List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
      boolean ignoredAnyEntries = false;
      //1:再次重新遍歷,只不過,這次model變成了Uri.class
      for (Entry<?, ?> entry : entries) {
        if (alreadyUsedEntries.contains(entry)) {
          ignoredAnyEntries = true;
          continue;
        }
        //2:同時滿足modelClass和dataClass才是要查找的ModelLoader
        if (entry.handles(modelClass, dataClass)) {
          alreadyUsedEntries.add(entry);
          loaders.add(this.<Model, Data>build(entry));
          alreadyUsedEntries.remove(entry);
        }
      }
      //3:如果多于1個loader,創(chuàng)建個兼容的多Model加載器MultiModelLoader
      if (loaders.size() > 1) {
        return factory.build(loaders, throwableListPool);
      } else if (loaders.size() == 1) {
        //4:只有1個,直接返回
        return loaders.get(0);
      } else {
        if (ignoredAnyEntries) {
          return emptyModelLoader();
        } else {
          throw new NoModelLoaderAvailableException(modelClass, dataClass);
        }
      }
    } catch (Throwable t) {
      alreadyUsedEntries.clear();
      throw t;
    }
  }

遍歷邏輯基本于上一個build相同,只不過,最后返回的時候,如果遍歷到多個ModelLoader會創(chuàng)建一個MultiModelLoader,用來保存多個ModelLoader,其實也是一個協(xié)調(diào)器,類似新建Request是的多個類型Request時,用一個協(xié)調(diào)器來包裹和協(xié)調(diào)。

我們依舊把model類型為Uri,data轉(zhuǎn)換類型為InputStream和ParcelFileDescriptor的ModelLoader用表格列出來:

model data Factory
Uri.class InputStream.class HttpUriLoader.Factory
Uri.class InputStream.class UriLoader.StreamFactory
Uri.class InputStream.class AssetUriLoader.StreamFactory
Uri.class InputStream.class MediaStoreImageThumbLoader.Factory
Uri.class InputStream.class MediaStoreVideoThumbLoader.Factory
Uri.class ParcelFileDescriptor.class AssetUriLoader.FileDescriptorFactory

這里列出了大體的ModelLoader,根據(jù)參數(shù)不一樣其工廠也會有些差別。

經(jīng)過這么一轉(zhuǎn)換,分化出來的ModelLoader就有七八個。但是每個loader都有其對應(yīng)可以加載數(shù)據(jù)類型,又或者在構(gòu)建LoadData的時候會有所判斷,來真正確認(rèn)是否可以加載目標(biāo)類型數(shù)據(jù)。

當(dāng)然,你可能會想,這么多的loader是否是又會分化出更多的ModelLoader出來?那么我要告訴你的是,bingo~,答對了!

但是不用擔(dān)心,萬變不離其宗,所有的分化,到最后都會有個實際干活的Loader。

這里我們加載的是一張網(wǎng)絡(luò)圖片,可想而知,最篩選出來的只有可以加載網(wǎng)絡(luò)數(shù)據(jù)的ModelLoader,判別方法也很簡單,通過handlers方法就可以判別出來,具體就不再展開了。這里能加載網(wǎng)絡(luò)圖片的有HttpUriLoader和UriLoader

以HttpUriLoader為例:

  //HttpUriLoader.java
  private static final Set<String> SCHEMES =
      Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));

  @Override
  public boolean handles(Uri model) {
    return SCHEMES.contains(model.getScheme());
  }
  
  //HttpUriLoader#Factory.java
  public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
  }

看到?jīng)],又繼續(xù)轉(zhuǎn)換了,這回變成了GlideUrl和InputStream,但是這次不一樣了,因為這兩個構(gòu)成的Modeloader只有一個,并且還是實際干活的!

model data Factory
GlideUrl.class InputStream.class HttpGlideUrlLoader.Factory
 //HttpGlideUrlLoader#Factory.java
 
 public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new HttpGlideUrlLoader(modelCache);
 }

經(jīng)過這一些列遍歷和轉(zhuǎn)換構(gòu)建,最終,Glide將得到一個ModelLoader列表,這個列表可能包含ModelLoader或者M(jìn)ultiModelLoader,取決于要加載的Model數(shù)據(jù)在注冊表中注冊的ModelLoad,以及ModelLoader相互間可發(fā)生的轉(zhuǎn)換。

2)構(gòu)建LoadData

讓我們回到DecodeHelper:

List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      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);
        //構(gòu)建LoadData
        LoadData<?> current =
            modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

得到ModelLoader后,將利用這些ModelLoader逐個構(gòu)建LoadData

首先,看MultiModelLoader是如何構(gòu)建LoadData的

//MultiModelLoader.java

public LoadData<Data> buildLoadData(Model model, int width, int height,
      Options options) {
    Key sourceKey = null;
    int size = modelLoaders.size();
    List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
      ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
      if (modelLoader.handles(model)) {
        LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
        if (loadData != null) {
          sourceKey = loadData.sourceKey;
          fetchers.add(loadData.fetcher);
        }
      }
    }
    return !fetchers.isEmpty()
        ? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)) : null;
}

MultiModelLoader會遍歷保存的ModelLoader列表,逐個構(gòu)建LoadData,并將各個LoadData中的DataFetcher取出,存放在MultiFetcher中,從而MultiFetcher又成為一個協(xié)調(diào)器。

再來看單個ModelLoader構(gòu)建LoadData,同樣的,以Model為String為例

  //StringLoader.java
  
  public LoadData<Data> buildLoadData(String model, int width, int height,
      Options options) {
    //將String轉(zhuǎn)換為Uri
    Uri uri = parseUri(model);
    return uri == null ? null : uriLoader.buildLoadData(uri, width, height, options);
  }

還記得上面StringLoader構(gòu)建的時候,進(jìn)行Uri轉(zhuǎn)換嗎?此處的uriLoader正是轉(zhuǎn)換后的ModelLoader/MultiLoader。其實最后構(gòu)建的就是實際干活的對象:HttpGlideUrlLoader

  //HttpGlideUrlLoader.java
  
  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));
  }

最后的代碼,將新建的HttpUrlFetcher注入給了LoadData,至此,得到一個有效的LoadData和DataFetcher。

3)數(shù)據(jù)抓取

得到了LoadData后,回到SourceGenerotor中,

  public boolean startNext() {
  
    //省略部分代碼...
    
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

通過循環(huán)遍歷所有的LoadData,并通過其DataFetcher,就可以進(jìn)行數(shù)據(jù)的抓取了。

  //HttpUrlFetcher.java
  
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

通過loadDataWithRedirects方法,利用HttpURLConnection就可以抓取到網(wǎng)絡(luò)圖片的InputStream,具體不再展開,有興趣可以查看源碼。

最后通過callback.onDataReady(result)將結(jié)果回調(diào)給SourceGenerator進(jìn)行下一步處理。

  //SourceGenerator.java
  
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

獲取到數(shù)據(jù)后,分兩種情況,一是需要將圖片緩存到本地硬盤,一種是不需要緩存。Glide配置了多種緩存策略,默認(rèn)是自動智能切換緩存存儲策略,Glide認(rèn)為遠(yuǎn)程網(wǎng)絡(luò)圖片獲取是昂貴的,所以默認(rèn)網(wǎng)絡(luò)圖片是會緩存原圖的。而本地圖片,包括drawable/assets等是不會緩存原圖的。(當(dāng)然你也可以重新配置)

那么這里獲得的是網(wǎng)絡(luò)圖片,所以會進(jìn)入if中,而else則是直接將結(jié)果返回給DecodeJob進(jìn)行解碼了。

進(jìn)入if后,會將數(shù)據(jù)保存起來(留意dataToCache),然后重啟任務(wù)。

reschedule后,將會接連回調(diào)DecodeJob和EngineJob的reschedule方法,從而重新開啟DecodeJob任務(wù)。

  //DecodeJob.java
  @Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }
  
  //EngineJob.java
  @Override
  public void reschedule(DecodeJob<?> job) {
    getActiveSourceExecutor().execute(job);
  }

由于開啟的是同一個DecodeJob任務(wù),所以整個任務(wù)的內(nèi)容是會繼續(xù)得到執(zhí)行的。結(jié)果仍然是回到SourceGenerator的startNext方法中

而這個時候,就會進(jìn)入上面提到的另一個情況。

  //SourceGenerator.java
  
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      //1:先緩存數(shù)據(jù)
      cacheData(data);
    }

    //2:再重新執(zhí)行數(shù)據(jù)轉(zhuǎn)換和抓取
    if (sourceCacheGenerator != null &&
        sourceCacheGenerator.startNext()) {
      return true;
    }
    
    //省略循環(huán)遍歷代碼......
  }
  
  private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
      DataCacheWriter<Object> writer =
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      helper.getDiskCache().put(originalKey, writer);
    } finally {
      loadData.fetcher.cleanup();
    }
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

這時dataToCache將不再是null的,所以會將數(shù)據(jù)緩存到本地硬盤,并啟動另一個加載器DataCacheGenerator,而這個Generator正是用來加載緩存在本地的圖片的。

而DataCacheGenerator和ResourceCacheGenerator的原理與SourceGenerator基本是一致的,只不過一個用來加載原始的本地緩存圖,另一個用來加載處理過的本地緩存。

最后,來總結(jié)一下Glide整個的數(shù)據(jù)轉(zhuǎn)換與抓取流程:

  1. Glide利用線程池的方式,將每一個解碼過程都封裝為一次解碼任務(wù)。
  2. 整個數(shù)據(jù)抓取過程中,Glide會嘗試從內(nèi)存到處理過的圖片緩存,再到原圖緩存,最后到遠(yuǎn)程圖片等四個地方進(jìn)行數(shù)據(jù)加載。(這里的遠(yuǎn)程圖片包括drawable/assets等資源)
  3. 數(shù)據(jù)模型轉(zhuǎn)換時,根據(jù)Glide初始化時注冊的模型轉(zhuǎn)換注冊表,將原始model模型數(shù)據(jù)轉(zhuǎn)換為可能的數(shù)據(jù)模型,并嘗試使用這些模型來抓取數(shù)據(jù),直至抓取到數(shù)據(jù),或抓取失敗返回。
  4. Glide數(shù)據(jù)轉(zhuǎn)模型使得Glide有非常好的拓展性和重用性。
  5. 整個數(shù)據(jù)轉(zhuǎn)換和抓取流程非常復(fù)雜,但是只要抓住其中一個源頭,并跟蹤下去,其實還是非常清晰的,也可以看到Glide設(shè)計的優(yōu)雅高明之處。

以上,就是Glide數(shù)據(jù)模型轉(zhuǎn)換和抓取的流程分析,下一篇我們將進(jìn)入Glide的解碼和轉(zhuǎn)碼源碼分析。


Glide4源碼解析系列

[Glide4源碼解析系列]--1.Glide初始化
[Glide4源碼解析系列]--2.Glide數(shù)據(jù)模型轉(zhuǎn)換與數(shù)據(jù)抓取


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容