Android Glide源碼剖析系列(三)深入理解Glide圖片加載流程


Glide源碼剖析系列


為什么選擇Glide嗓蘑?

  • 多種圖片格式的緩存,適用于更多的內(nèi)容表現(xiàn)形式(如Gif、WebP桩皿、縮略圖豌汇、Video)
  • 生命周期集成(根據(jù)Activity或者Fragment的生命周期管理圖片加載請求)Glide可以感知調(diào)用頁面的生命周期,這就是優(yōu)勢
  • 高效處理Bitmap(bitmap的復(fù)用和主動回收泄隔,減少系統(tǒng)回收壓力)
  • 高效的緩存策略拒贱,靈活(Picasso只會緩存原始尺寸的圖片,Glide緩存的是多種規(guī)格)佛嬉,加載速度快且內(nèi)存開銷械羰摺(默認Bitmap格式的不同麻敌,使得內(nèi)存開銷是Picasso的一半)

小結(jié):支持圖片格式多;Bitmap復(fù)用和主動回收;生命周期感應(yīng)荐操;優(yōu)秀的緩存策略慕趴;加載速度快(Bitmap默認格式RGB565)

Glide簡單使用

Glide.with(this)
        .load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
        .error(R.drawable.aaa)
        .placeholder(R.drawable.ic_android_black_24dp)
        .fallback(R.drawable.aaa)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .skipMemoryCache(true)
        .into(imageView);

源碼分析

上篇文章學(xué)習了RequestTracker 如何管理圖片加載請求眨业,本文開始分析圖片加載請求執(zhí)行流程推盛。

#RequestTracker.java
  /** Starts tracking the given request. 把請求添加到requests和pendingRequests*/
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();  //圖片加載請求執(zhí)行入口
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

request.begin()方法是請求開始執(zhí)行的入口,這里的request是SingleRequest類型钝腺,所以有必要先搞懂SingleRequest類

/**
 * 定義:把資源加載到目標的請求
 */
public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {

  //請求的狀態(tài)
  private enum Status {
    /** Created but not yet running. */
    PENDING,
    /** In the process of fetching media. */
    RUNNING,
    /** Waiting for a callback given to the Target to be called to determine target dimensions. */
    WAITING_FOR_SIZE,
    /** Finished loading media successfully. */
    COMPLETE,
    /** Failed to load media, may be restarted. */
    FAILED,
    /** Cleared by the user with a placeholder set, may be restarted. */
    CLEARED,
  }

  /* 同步鎖對象:給請求的操作方法上鎖抛姑,保證線程安全 */
  private final Object requestLock;
  //監(jiān)聽請求的狀態(tài)變化
  @Nullable private final RequestListener<R> targetListener;
  //協(xié)調(diào)一個target上的多個請求
  private final RequestCoordinator requestCoordinator;

  private final Context context;

  private final GlideContext glideContext;
  
  @Nullable private final Object model;

  private final Class<R> transcodeClass;
  //請求的配置參數(shù)
  private final BaseRequestOptions<?> requestOptions;

  private final int overrideWidth;

  private final int overrideHeight;

  private final Priority priority;

  private final Target<R> target;

  @Nullable private final List<RequestListener<R>> requestListeners;

  private final TransitionFactory<? super R> animationFactory;

  private final Executor callbackExecutor;

  @GuardedBy("requestLock")
  private Resource<R> resource;  

  @GuardedBy("requestLock")
  private Engine.LoadStatus loadStatus;

  @GuardedBy("requestLock")
  private long startTime;

  // Volatile because it's accessed outside of a lock and nullable, even though in practice it will
  // always be non-null unless the request is in the object pool.
  private volatile Engine engine;  //圖片處理引擎

  /* Variables mutated during a request. */
  @GuardedBy("requestLock")
  private Status status;

  @GuardedBy("requestLock")
  @Nullable
  private Drawable errorDrawable;  //加載失敗顯示圖

  @GuardedBy("requestLock")
  @Nullable
  private Drawable placeholderDrawable;  //占位圖

  @GuardedBy("requestLock")
  @Nullable
  private Drawable fallbackDrawable;

  @GuardedBy("requestLock")
  private int width;

  @GuardedBy("requestLock")
  private int height;

  @Override
  public void begin() {
    synchronized (requestLock) {  //加鎖
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
      if (model == null) {  //注釋1
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
          width = overrideWidth;
          height = overrideHeight;
        }
        // Only log at more verbose log levels if the user has set a fallback drawable, because
        // fallback Drawables indicate the user expects null models occasionally.
        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
        onLoadFailed(new GlideException("Received null model"), logLevel);   //加載失敗
        return;
      }

      if (status == Status.RUNNING) {  //注釋2
        throw new IllegalArgumentException("Cannot restart a running request"); //直接拋出異常
      }

      // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
      // that starts an identical request into the same Target or View), we can simply use the
      // resource and size we retrieved the last time around and skip obtaining a new size, starting
      // a new load etc. This does mean that users who want to restart a load because they expect
      // that the view size has changed will need to explicitly clear the View or Target before
      // starting the new load.
      if (status == Status.COMPLETE) {  //注釋3
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      }

      // Restarts for requests that are neither complete nor running can be treated as new requests
      // and can run again from the beginning.

      experimentalNotifyRequestStarted(model);

      cookie = GlideTrace.beginSectionAsync(TAG);
      status = Status.WAITING_FOR_SIZE;  //狀態(tài)設(shè)為:等待target確定尺寸
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {  //注釋4
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {  //target設(shè)置占位圖
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }
}

逐個分析Request#begin()方法中的if判斷語句

  • 注釋1:如果資源數(shù)據(jù)為null,執(zhí)行onLoadFailed(new GlideException("Received null model"), logLevel)方法艳狐;
  private void onLoadFailed(GlideException e, int maxLogLevel) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {  //加鎖
      loadStatus = null;
      status = Status.FAILED;  //1.設(shè)置請求的狀態(tài)

      isCallingCallbacks = true;
      try {
        // TODO: what if this is a thumbnail request?
        boolean anyListenerHandledUpdatingTarget = false;
        if (requestListeners != null) {
          for (RequestListener<R> listener : requestListeners) {
            anyListenerHandledUpdatingTarget |=
                listener.onLoadFailed(e, model, target, isFirstReadyResource());
          }
        }
        anyListenerHandledUpdatingTarget |=
            targetListener != null
                && targetListener.onLoadFailed(e, model, target, isFirstReadyResource());

        if (!anyListenerHandledUpdatingTarget) {
          setErrorPlaceholder();  //2.如果不是縮略圖請求定硝,設(shè)置顯示失敗占位圖
        }
      } finally {
        isCallingCallbacks = false;
      }

      notifyLoadFailed();
    }
  }

  @GuardedBy("requestLock")
  private void setErrorPlaceholder() {
    if (!canNotifyStatusChanged()) {
      return;
    }

    Drawable error = null;
    if (model == null) {
      error = getFallbackDrawable();
    }
    // Either the model isn't null, or there was no fallback drawable set.
    if (error == null) {
      error = getErrorDrawable();
    }
    // The model isn't null, no fallback drawable was set or no error drawable was set.
    if (error == null) {
      error = getPlaceholderDrawable();
    }
    target.onLoadFailed(error);  //3.告訴target資源加載失敗,并把錯誤占位圖資源回傳給target
  }
  1. 設(shè)置請求的狀態(tài)為Status.FAILED毫目;
  2. 如果不是縮略圖請求蔬啡,setErrorPlaceholder()設(shè)置顯示失敗占位圖;
  3. 告訴target資源加載失敗镀虐,并把錯誤占位圖資源回傳給target
  • 注釋2:請求狀態(tài)為RUNNING箱蟆,不允許執(zhí)行begin()方法
  • 注釋3:請求狀態(tài)為COMPLETE,執(zhí)行onResourceReady( resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
  @Override
  public void onResourceReady(
      Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
        if (resource == null) {  //資源為null刮便,回調(diào)onLoadFailed
          GlideException exception =
              new GlideException(
                  "Expected to receive a Resource<R> with an "
                      + "object of "
                      + transcodeClass
                      + " inside, but instead got null.");
          onLoadFailed(exception);
          return;
        }

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

        if (!canSetResource()) {
          toRelease = resource;  //準備回收resource
          this.resource = null;
          // We can't put the status to complete before asking canSetResource().
          status = Status.COMPLETE;
          GlideTrace.endSectionAsync(TAG, cookie);
          return;
        }

        onResourceReady(
            (Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
      }
    } finally {
      if (toRelease != null) {
        engine.release(toRelease);
      }
    }
  }

  @GuardedBy("requestLock")
  private boolean canSetResource() {
    return requestCoordinator == null || requestCoordinator.canSetImage(this);
  }
  1. 如果 resource 或 resource.get() 為空空猜,調(diào)用 onLoadFailed(exception);
  2. 如果不允許設(shè)置resource,請求狀態(tài)設(shè)為COMPLETE恨旱,并準備回收resource
  3. 執(zhí)行onResourceReady( (Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
  @GuardedBy("requestLock")
  private void onResourceReady(
      Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
    GlideTrace.endSectionAsync(TAG, cookie);
  }

資源加載成功回調(diào)target.onResourceReady()方法

  • 注釋4:如果通過RequestOption設(shè)置的寬高都大于0或者與Target原始寬高相等辈毯,則調(diào)用onSizeReady(overrideWidth, overrideHeight);;否則重新計算target尺寸搜贤,計算完成后依然會調(diào)用onSizeReady()方法
  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;  //請求的狀態(tài)設(shè)為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,
              callbackExecutor);

      // This is a hack that's only useful for testing right now where loads complete synchronously
      // even though under any executor running on any thread but the main thread, the load would
      // have completed asynchronously.
      if (status != Status.RUNNING) {
        loadStatus = null;
      }
    }
  }
  1. 請求的狀態(tài)設(shè)為RUNNING
  2. 圖片加載引擎Engine開始加載圖片

到此為止谆沃,Request的begin()方法分析完畢,圖片加載流程最終交給Engine執(zhí)行仪芒。Engine是整個圖片加載流程中一個非常重要的角色唁影,接下來繼續(xù)閱讀源碼揭開Engine的神秘面紗耕陷。

#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,
      Executor callbackExecutor) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

    //使用request配置信息構(gòu)建EngineKey,用于資源緩存
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);  //從內(nèi)存緩存中查找資源

      if (memoryResource == null) {  //緩存中查找不到資源据沈,重用或新建一個新EngineJob
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

    // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
    // deadlock.
    cb.onResourceReady(
        memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
    return null;
  }

 @Nullable
  private EngineResource<?> loadFromMemory(
      EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> active = loadFromActiveResources(key);  //正在使用的資源列表中查找
    if (active != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return active;
    }

    EngineResource<?> cached = loadFromCache(key);  //內(nèi)存緩存中查找資源
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }

    return null;
  }
  1. 使用request配置信息構(gòu)建EngineKey啃炸,用作資源緩存時的key
  2. 從正在使用的資源列表和內(nèi)存緩存中查找資源
  3. 如果沒找到資源,重用或新建一個新EngineJob

注:Glide著名的圖片緩存分為內(nèi)存緩存和磁盤緩存卓舵,這里已經(jīng)出現(xiàn)了內(nèi)存緩存,磁盤緩存會在接下來的DecodeJob中處理膀钠。關(guān)于圖片緩存機制會專門擼一篇文章掏湾,所以本文中會略過緩存處理相關(guān)邏輯。

  private <R> LoadStatus waitForExistingOrStartNewJob(
      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,
      EngineKey key,
      long startTime) {

    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

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

    jobs.put(key, engineJob);

    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }

從EngineJob緩存列表中查找是否有可重用的EngineJob肿嘲,如果有直接重用融击;否則新建一個EngineJob,并開啟該JobengineJob.start(decodeJob);

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

解碼工作是耗時操作雳窟,不能在主線程操作尊浪,因此把decodeJob提交到線程池執(zhí)行

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback, Runnable,
 Comparable<DecodeJob<?>>, Poolable {
  @Override
  public void run() {
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (CallbackException e) {
      throw e;
    } catch (Throwable t) {
      // 異常處理
    } finally {
      //清理工作
    }
  }
}

  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);  //1
        currentGenerator = getNextGenerator();  //2
        runGenerators();  //3
        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) {
      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);
    }
  }

  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);  //緩存的處理過的資源的生成器
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);  //緩存的原始資源的生成器
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);  //使用modelLoader和model獲取原始資源的生成器
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }
  1. 計算nextStage
  2. 根據(jù)nextStage獲取對應(yīng)的Generator
  3. 執(zhí)行runGenerators()
  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();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }

while循環(huán)語句:圖片可能來自于磁盤緩存,也可能需要開啟任務(wù)去獲取封救。我們分析的是沒有緩存的情況拇涤,所以要先確定使用哪種Generator來生成資源,然后繼續(xù)查看加載流程誉结,此處以SourceGenerator為例:

繼續(xù)分析sourceGenerator.startNext()

#SourceGenerator.java
  @Override
  public boolean startNext() {
   //跳過緩存相關(guān)邏輯鹅士,分析沒有緩存時的加載流程
    while (!started && hasNextModelLoader()) {  //注釋5
      loadData = helper.getLoadData().get(loadDataListIndex++);  
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        startNextLoad(loadData);
      }
    }
    return started;
  }

我們知道資源加載由ModelLoader才能完成,先分析一下ModelLoader類

public interface ModelLoader<Model, Data> {

  class LoadData<Data> {
    public final Key sourceKey;  //用于緩存的key
    public final List<Key> alternateKeys;
    public final DataFetcher<Data> fetcher;  //數(shù)據(jù)抓取器

    public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
      this(sourceKey, Collections.<Key>emptyList(), fetcher);
    }

    public LoadData(
        @NonNull Key sourceKey,
        @NonNull List<Key> alternateKeys,
        @NonNull DataFetcher<Data> fetcher) {
      this.sourceKey = Preconditions.checkNotNull(sourceKey);
      this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
      this.fetcher = Preconditions.checkNotNull(fetcher);
    }
  }

  @Nullable
  LoadData<Data> buildLoadData(
      @NonNull Model model, int width, int height, @NonNull Options options);

  boolean handles(@NonNull Model model);
}

原來真正干活的是內(nèi)部類LoadData惩坑,其中的sourceKey用于緩存資源掉盅,fetcher是數(shù)據(jù)抓取器。

回到sourceGenerator.startNext()注釋5循環(huán)語句:

#DecodeHelper.java
  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      //noinspection ForLoopReplaceableByForEach to improve perf
      for (int i = 0, size = modelLoaders.size(); 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;
  }

根據(jù)model類型從ModelLoader注冊列表中篩選出符合要求的ModelLoader列表以舒,最終篩選出能干活的LoadData去執(zhí)行startNextLoad(loadData)

#SourceGenerator.java
  private void startNextLoad(final LoadData<?> toStart) {
    //注釋6:加載資源
    loadData.fetcher.loadData(  
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {  
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }
注釋6:最終加載工作交由Fetcher處理趾痘,由于我們傳入的model是String類型的圖片地址,所以這里干活的是HttpUrlFetcher蔓钟,進入HttpUrlFetcher#loadData():
#HttpUrlFetcher.java
  @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      callback.onLoadFailed(e);
    } finally {
    }
  }

使用loadDataWithRedirects()方法獲取網(wǎng)絡(luò)資源的InputStream永票,并且把結(jié)果回調(diào)給callback.onDataReady(result)方法,代碼執(zhí)行到注釋6處的onDataReady()回調(diào)方法奋刽,內(nèi)部調(diào)用onDataReadyInternal(toStart, data) 繼續(xù)處理inputStream

#SourceGenerator.java
  @Synthetic
  void onDataReadyInternal(LoadData<?> loadData, 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);
    }
  }

由于我們是禁用緩存的瓦侮,直接進入到else語句,這里又是一個方法回調(diào)佣谐,這個回調(diào)方法在DecodeJob中實現(xiàn)

#DecodeJob.java
  @Override
  public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);

    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

這里需要經(jīng)歷一系列的方法調(diào)用來處理數(shù)據(jù):decodeFromRetrievedData() -> decodeFromData(currentFetcher, currentData, currentDataSource) -> decodeFromFetcher(data, dataSource)

#DecodeJob.java
  @SuppressWarnings("unchecked")
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());  //根據(jù)data類型獲取LoadPath
    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);
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // ResourceType in DecodeCallback below is required for compilation to work with gradle.
      return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }
#LoadPath.java
  public Resource<Transcode> load(
      DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width,
      int height,
      DecodePath.DecodeCallback<ResourceType> decodeCallback)
      throws GlideException {
    List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
    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;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {
        break;
      }
    }
    if (result == null) {
      throw new GlideException(failureMessage, new ArrayList<>(exceptions));
    }
    return result;
  }
#DecodePath.java
  public Resource<Transcode> decode(
      DataRewinder<DataType> rewinder,
      int width,
      int height,
      @NonNull Options options,
      DecodeCallback<ResourceType> callback)
      throws GlideException {
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
  }

目標資源經(jīng)過一系列解碼(decode)和轉(zhuǎn)換(transform)操作肚吏,最終得到我們需要的BitmapDrawableResource并一層層返回給調(diào)用發(fā)起的地方,也就是decodeFromRetrievedData()方法狭魂,我們回到該方法:

  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 {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey);
    } else {
      runGenerators();
    }
  }

把decode得到的結(jié)果resource傳入notifyEncodeAndRelease()方法

  private void notifyEncodeAndRelease(
      Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
    try {
      if (resource instanceof Initializable) {
        ((Initializable) resource).initialize();  //1
      }

      Resource<R> result = resource;
      LockedResource<R> lockedResource = null;
      if (deferredEncodeManager.hasResourceToEncode()) {
        lockedResource = LockedResource.obtain(resource);
        result = lockedResource;
      }

      notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey);  //2

      stage = Stage.ENCODE;
      try {
        if (deferredEncodeManager.hasResourceToEncode()) {
          deferredEncodeManager.encode(diskCacheProvider, options);  
        }
      } finally {
        if (lockedResource != null) {
          lockedResource.unlock();
        }
      }
      // Call onEncodeComplete outside the finally block so that it's not called if the encode
      // process
      // throws.
      onEncodeComplete();
    } finally {
      GlideTrace.endSection();
    }
  }
  1. 執(zhí)行繪制Bitmap的準備工作
  @Override
  public void initialize() {
    drawable.getBitmap().prepareToDraw();
  }
  1. 圖片資源decode完畢罚攀,可以拿去顯示圖片啦

跨越千山萬水馬上到達終點了党觅,懷著激動的心情繼續(xù)分析代碼:

  private void notifyComplete(
      Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
  }

onResourceReady()方法在EngineJob中實現(xiàn)。繼續(xù)追蹤代碼:

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

  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      //我們省略了異常情況的處理代碼和一些注釋代碼

      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener); //1
     
      hasResource = true;
      copy = cbs.copy();  //2
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }

    engineJobListener.onEngineJobComplete(this, localKey, localResource);  //3

    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));  //4
    }
    decrementPendingCallbacks();
  }
  1. engineResource 是資源的一個包裝類斋泄,負責計算資源被引用的次數(shù)杯瞻,次數(shù)為0的時候可以回收資源
  2. copy內(nèi)部包裝的是Executors.mainThreadExecutor()主線程池,方便切換到主線程
  3. EngineJob執(zhí)行完畢炫掐,把加載的資源加入內(nèi)存緩存并且從EngineJob緩存列表移除當前job
  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null && resource.isMemoryCacheable()) {
      activeResources.activate(key, resource);
    }

    jobs.removeIfCurrent(key, engineJob);
  }
  1. 把任務(wù)切換到主線程執(zhí)行魁莉,也就是說之前的操作都是在子線程中處理
    @Override
    public void run() {
      // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
      // (b/136032534).
      synchronized (cb.getLock()) {
        synchronized (EngineJob.this) {
          if (cbs.contains(cb)) {
            // Acquire for this particular callback.
            engineResource.acquire();  //增加資源引用次數(shù)
            callCallbackOnResourceReady(cb);  //繼續(xù)回調(diào)
            removeCallback(cb);
          }
          decrementPendingCallbacks();
        }
      }
    }

  @GuardedBy("this")
  void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      // This is overly broad, some Glide code is actually called here, but it's much
      // simpler to encapsulate here than to do so at the actual call point in the
      // Request implementation.
      cb.onResourceReady(engineResource, dataSource, isLoadedFromAlternateCacheKey);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }

最終還是回到了SingleRequest的onResourceReady(),也就是文章開頭介紹的begin()方法注釋3部分募胃,太遠了重新貼一下代碼吧旗唁!

 @GuardedBy("requestLock")
  private void onResourceReady(
      Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      //1 begin
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
    //1 end

      if (!anyListenerHandledUpdatingTarget) {  //2
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);  
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
    GlideTrace.endSectionAsync(TAG, cookie);
  }
  1. 如果設(shè)置了requestListener或targetListener,則調(diào)用它們的onResourceReady()回調(diào)方法痹束;
  2. anyListenerHandledUpdatingTarget含義:是否有l(wèi)istener處理检疫,即listener.onResourceReady()方法的返回值。
    如果anyListenerHandledUpdatingTarget==false祷嘶,進入ImageViewTarget#onResourceReady()方法:
  @Override
  public void onResourceReady(@NonNull 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) {
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);  //抽象方法屎媳,子類中實現(xiàn)
    maybeUpdateAnimatable(resource);
  }

進入DrawableImageViewTarget#setResource(resource)方法:

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

最終資源被成功設(shè)置到ImageView上,圖片加載流程結(jié)束论巍。

圖片加載流程圖

如果有錯誤或理解不到位的地方烛谊,歡迎批評指正。

Tips:整個加載流程的代碼調(diào)用真的是很復(fù)雜环壤,涉及到Callback回調(diào)晒来、抽象方法實現(xiàn)、還有參數(shù)N次傳遞后很難定位來源郑现,建議一邊調(diào)試程序一邊跟蹤代碼湃崩,可以直接跳轉(zhuǎn)到執(zhí)行點。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末接箫,一起剝皮案震驚了整個濱河市攒读,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辛友,老刑警劉巖薄扁,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異废累,居然都是意外死亡邓梅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門邑滨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來日缨,“玉大人,你說我怎么就攤上這事掖看∠痪啵” “怎么了面哥?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毅待。 經(jīng)常有香客問我尚卫,道長,這世上最難降的妖魔是什么尸红? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任吱涉,我火速辦了婚禮,結(jié)果婚禮上外里,老公的妹妹穿的比我還像新娘邑飒。我一直安慰自己,他們只是感情好级乐,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著县匠,像睡著了一般风科。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乞旦,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天贼穆,我揣著相機與錄音,去河邊找鬼兰粉。 笑死故痊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的玖姑。 我是一名探鬼主播愕秫,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼焰络!你這毒婦竟也來了戴甩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤闪彼,失蹤者是張志新(化名)和其女友劉穎甜孤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畏腕,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡缴川,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了描馅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片把夸。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖流昏,靈堂內(nèi)的尸體忽然破棺而出扎即,到底是詐尸還是另有隱情吞获,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布谚鄙,位于F島的核電站各拷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闷营。R本人自食惡果不足惜烤黍,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望傻盟。 院中可真熱鬧速蕊,春花似錦、人聲如沸娘赴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诽表。三九已至唉锌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間竿奏,已是汗流浹背袄简。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泛啸,地道東北人绿语。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像候址,于是被迫代替她去往敵國和親吕粹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

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