Glide源碼解析-加載流程

1 引言

一直想要閱讀Glide源碼,可是苦於時間和功力都不夠,老是斷斷續續的,趁着如今有一些空暇時間,來簡要分析Glide的源碼。Glide的實現太過複雜,不可能作到面面俱到,若是每一行都細緻分析,很容易陷入做者的優化細節中去而偏離主線,所以只針對幾個主要功能作解析便可。 如下分析所有基於Glide v4.9.0。java

2 初始化

Glide最多見的用法就是以下一行代碼:git

Glide.with(context).load(url).into(imageView);
複製代碼

一步步來分析如何將url圖片加載到imageview上來。github

2.1 with

public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}
public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
  return getRetriever(view.getContext()).get(view);
}
複製代碼

with方法是Glide類中的一組同名static重載函數,能夠傳入多種上下文,方法體內是調用getRetriever得到RequestManagerRetriever實例對象,再調用其get方法返回一個RequestManager實例。緩存

private static volatile Glide glide;

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  // Context could be null for other reasons (ie the user passes in null), but in practice it will
  // only occur due to errors with the Fragment lifecycle.
  ...
  return Glide.get(context).getRequestManagerRetriever();
}

public static Glide get(@NonNull Context context) {
  if (glide == null) {
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context);
      }
    }
  }
  return glide;
}

public RequestManagerRetriever getRequestManagerRetriever() {
  return requestManagerRetriever;
}
複製代碼

這裏Glide的get方法用了DCL單例,而後拿到Glide的成員變量requestManagerRetriever。 而後再看RequestManagerRetriever類。網絡

@NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }
複製代碼

這是上文with方法裏調用的get方法,這裏會對傳入的context作判斷,若是方法調用是在主線程同時context不是Application,則會根據context的類型調用一組重載的get方法,不然就調用getApplicationManager。app

那這兩個分支有什麼區別呢?具體看一下兩個處理方法。框架

private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we're on a background thread.
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        // Normally pause/resume is taken care of by the fragment we add to the fragment or
        // activity. However, in this case since the manager attached to the application will not
        // receive lifecycle events, we must force the manager to start resumed using
        // ApplicationLifecycle.
        // TODO(b/27524013): Factor out this Glide.get() call.
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }
  return applicationManager;
}
public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
  SupportRequestManagerFragment current =
      (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingSupportRequestManagerFragments.get(fm);
    if (current == null) {
      current = new SupportRequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart();
      }
      pendingSupportRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}
複製代碼

這裏的區別主要是將glide和不一樣的上下文的聲明週期綁定,若是是Application或者不在主線程調用,那requetmanager的生命週期和Application相關,不然則會和當前頁面的fragmentManager的聲明週期相關。由於Activity下fragmentManager的生命週期和Activity相同。因此不論是Activity仍是fragment,最後都會委託給fragmentManager作生命週期的管理。ide

在getSupportRequestManagerFragment方法中能夠看到若是activity下的fragmentmanager沒有找到tag爲FRAGMENT_TAG的fragment,就會建立一個隱藏的fragment,而後添加到fragmentmanager內。函數

總結來講with方法的做用就是得到當前上下文,構造出和上下文生命週期綁定的requestmanager,自動管理glide的加載開始和中止。fetch

2.2 load

load方法也是一組重載方法,定義在interface ModelTypes<T>接口裏,這是一個泛型接口,規定了load想要返回的數據類型,RequestManager類實現了該接口,泛型爲Drawable類。

public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}
public RequestBuilder<Drawable> load(@Nullable File file) {
  return asDrawable().load(file);
}
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
  return asDrawable().load(uri);
}
public RequestBuilder<Drawable> load(@Nullable String string) {
  return asDrawable().load(string);
}
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
  return asDrawable().load(drawable);
}
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
  return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
  return asDrawable().load(resourceId);
}
public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) {
  return new RequestBuilder<>(glide, this, resourceClass, context);
}
......
複製代碼

RequestManager下的load方法都返回RequestBuilder對象,顯然是一個建造者模式,用來構建須要的屬性。asDrawable方法調用的as方法其實是調用RequestBuilder的構造方法。而後掉用RequestBuilder的load將須要加載的object傳遞給builder,而後load方法都會調用loadGeneric將不一樣的參數類型統一傳給Object類的model成員變量,若是傳遞的參數類型是Drawable或者Bitmap,那麼將會額外調用.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));,意味着這兩種類型將不會作緩存。

public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
  return loadGeneric(bitmap)
      .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  this.model = model;
  isModelSet = true;
  return this;
}
複製代碼

總結一下load做用,構造一個RequestBuilder實例,同時傳入須要加載的數據源類型。

2.3 into

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
  Util.assertMainThread();
  Preconditions.checkNotNull(view);
  BaseRequestOptions<?> requestOptions = this;
  //檢查是否額外設置了imageview的裁剪方法
  if (!requestOptions.isTransformationSet()
      && requestOptions.isTransformationAllowed()
      && view.getScaleType() != null) {
    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(
      //構建target
      glideContext.buildImageViewTarget(view, transcodeClass),
      /*targetListener=*/ null,
      requestOptions,
      Executors.mainThreadExecutor());
}

@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
  return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, Executor callbackExecutor) {
  return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
複製代碼

into方法一樣是在RequestBuilder內,是glide加載圖片流程的最後一步,他暴露了兩種public方法,一個的參數是imageview,做用是指定圖片最後要加載到的位置。另外一個參數是target對象,能夠定製化一個target並返回。

對於參數imageview的into方法,首先先檢查requestBuilder是否額外設置過imageview的scaletype屬性,若是有則在requestoption裏面加上裁剪選項,接着構建一個target實例並建立一個主線程的executor用於得到圖片資源在主線程更新UI,調用private的into方法。

建立target方法細節以下:

public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
        //工廠方法建立target的子類viewtarget
  return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
//根據傳入的不一樣class類型構造bitmap或者drawabletarget
  if (Bitmap.class.equals(clazz)) {
    return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
  } 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)");
  }
}

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
  public DrawableImageViewTarget(ImageView view) {
    super(view);
  }
  /** * @deprecated Use {@link #waitForLayout()} instead. */
  // Public API.
  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }
  //負責將最後得到Drawable資源加載到into指定的imageview上
  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
}
複製代碼

對於參數target的into方法,則是直接建立一個主線程的runnable用於回調target給主線程。

接下來看private的into方法作了什麼:

private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
  Preconditions.checkNotNull(target);
  //判斷是否調用過load方法設置目資源
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }
  //構建Request實例
  Request request = buildRequest(target, targetListener, options, callbackExecutor);
  Request previous = target.getRequest();
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    request.recycle();
    if (!Preconditions.checkNotNull(previous).isRunning()) {
    //使用上一個請求而不是新請求來容許優化,例如跳過設置佔位符,跟蹤和取消跟蹤目標,以及獲取在單個請求中完成的查看維度。
      previous.begin();
    }
    return target;
  }
  //取消掛起的任務 清除資源 達到複用目的
  requestManager.clear(target);
  //爲target綁定request
  target.setRequest(request);
  //執行request
  requestManager.track(target, request);
  return target;
}
複製代碼

先判斷是否調用過load方法設置目標資源變量,若是沒有直接拋出異常,接着構建request實例,同時得到target上的前一個request(參數爲imageview的into方法跳過),若是相同則直接複用前一個request,免去了一些配置步驟,同時爲了能順利完成回調,增長了重試機制。而後對於imageview來講會先取消以前掛起的任務清楚任務資源,而後爲target從新綁定request請求,track方法開始執行request任務。

看一下最後track方法作了什麼:

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  //將target加入到追蹤隊列
  targetTracker.track(target);
  //執行request請求
  requestTracker.runRequest(request);
}
複製代碼

首先會將目標target加入到追蹤隊列,這個隊列裏保存了當前activity裏全部的target,同時和生命週期進行了綁定,這樣作的好處是用生命週期自動管理了request請求的開始、暫停、結束等操做。

public final class TargetTracker implements LifecycleListener {
  private final Set<Target<?>> targets =
      Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
  public void track(@NonNull Target<?> target) {
    targets.add(target);
  }
  public void untrack(@NonNull Target<?> target) {
    targets.remove(target);
  }
  @Override
  public void onStart() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onStart();
    }
  }
  @Override
  public void onStop() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onStop();
    }
  }
  @Override
  public void onDestroy() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onDestroy();
    }
  }
  @NonNull
  public List<Target<?>> getAll() {
    return Util.getSnapshot(targets);
  }
  public void clear() {
    targets.clear();
  }
}
複製代碼

runRequest方法則是最終真正執行加載圖片資源的操做:

public void runRequest(@NonNull Request request) {
  requests.add(request);
  //request隊列是否處於暫停狀態
  if (!isPaused) {
    //若是不則執行request
    request.begin();
  } else {
    //清楚request資源
    request.clear();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Paused, delaying request");
    }
    //加入掛起隊列
    pendingRequests.add(request);
  }
}
複製代碼

begin操做是調用了SingleRequest類的begin方法,SingleRequest實現了Request接口:

public synchronized void begin() {
  assertNotCallingCallbacks();
  stateVerifier.throwIfRecycled();
  startTime = LogTime.getLogTime();
  //若是
  if (model == null) {
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      width = overrideWidth;
      height = overrideHeight;
    }
    int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
    onLoadFailed(new GlideException("Received null model"), logLevel);
    return;
  }
  //正在運行 重複執行Request拋出異常
  if (status == Status.RUNNING) {
    throw new IllegalArgumentException("Cannot restart a running request");
  }
  //若是咱們在完成以後從新啓動(一般經過諸如notifyDataSetChanged之類的東西,在相同的目標或視圖中啓動相同的請求),咱們能夠簡單地使用咱們上次檢索的資源和大小,並跳過獲取新的大小
  //這意味着想要從新啓動負載由於指望視圖大小已更改的用戶須要在開始新加載以前明確清除視圖或目標。
  if (status == Status.COMPLETE) {
    onResourceReady(resource, DataSource.MEMORY_CACHE);
    return;
  }
  // Restarts for requests that are neither complete nor running can be treated as new requests
  // and can run again from the beginning.
  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());
  }
}
複製代碼

方法裏首先判斷是否設置過model,若是沒有則直接回調加載失敗,而後判斷是否正在運行,若是重複請求就直接拋出異常,接着判斷是否Request已經完成,完成則調用onResourceReady,接着給view肯定height和width,同時調用onSizeReady,若是狀態處於running或者WAITING_FOR_SIZE,調用onLoadStarted

這三個回調的名字很明顯,分別對應Request的三個過程,onSizeReady(準備)、onLoadStarted(開始)、onResourceReady(資源完成),一個個來看。

2.3.1 onSizeReady

public synchronized void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  //若是進入時不是WAITING_FOR_SIZE直接退出
  if (status != Status.WAITING_FOR_SIZE) {
    return;
  }
  //狀態調整至running
  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,
          callbackExecutor);
  if (status != Status.RUNNING) {
    loadStatus = null;
  }
}
複製代碼

load方法就是真正執行加載資源的代碼,裏面有一個runWrapped方法:

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 enum Stage {
  /** The initial stage. */
  INITIALIZE,
  /** Decode from a cached resource. */
  RESOURCE_CACHE,
  /** Decode from cached source data. */
  DATA_CACHE,
  /** Decode from retrieved source. */
  SOURCE,
  /** Encoding transformed resources after a successful load. */
  ENCODE,
  /** No more viable stages. */
  FINISHED,
}
複製代碼

這裏作了一個狀態機的轉換,按照Stage的流程不斷的流轉,若是runReason是INITIALIZE,就獲取Stage.INITIALIZE的下一個狀態,先從RESOURCE_CACHE內存裏獲取緩存,再從DATA_CACHE磁盤獲取緩存,再從source數據源取數據。這就是三級緩存的策略。

三級緩存的生成對應着三個生成類,經過調用getNextGenerator方法獲取:

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);
    case FINISHED:
      return null;
    default:
      throw new IllegalStateException("Unrecognized stage: " + stage);
  }
}
複製代碼

獲取到DataFetcherGenerator以後,就會調用runGenerators方法去執行獲取數據操做,startNext方法就是內部獲取數據代碼。

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();
  }
}
複製代碼

來看一下SourceGenerator的startNext(),會調用loadData方法,根據不一樣的獲取資源策略加載數據,在HttpUrlFetcher類裏也就是網絡請求數據的loadData方法中,會請求url拿到輸入流,而後回調給Generator,Generator的onDataReady方法接收到回調以後會根據緩存策略選擇將數據緩存起來或是回調數據給外部。

public boolean startNext() {
    //更新緩存
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }
  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
    return true;
  }
  sourceCacheGenerator = null;
  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    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;
}

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

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);
  }
}
複製代碼

2.3.2 onLoadStarted

當status的狀態爲running或者WAITING_FOR_SIZE的時候,就會調用該方法,它會調用target的onLoadStarted作一些準備工做,在ImageViewTarget類中就會設置placeholder和一些加載動畫。

public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
}
複製代碼

2.3.3 onResourceReady

這個方法就是最後將得到數據裝進imageview或者返回給target的方法:

private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
  // We must call isFirstReadyResource before setting status.
  boolean isFirstResource = isFirstReadyResource();
  status = Status.COMPLETE;
  this.resource = resource;
  if (glideContext.getLogLevel() <= Log.DEBUG) {
    Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
        + dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
        + LogTime.getElapsedMillis(startTime) + " ms");
  }
  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);
    // 1
      target.onResourceReady(result, animation);
    }
  } finally {
    isCallingCallbacks = false;
  }
  notifyLoadSuccess();
}
複製代碼

重點看一下注釋1出的target.onResourceReady,它將獲取到的數據經過target調用塞入target中進行加載,看一下ImageViewTarget的方法:

public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    //沒有變化 則直接setresource
  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);
  maybeUpdateAnimatable(resource);
}

protected void setResource(@Nullable Drawable resource) {
  view.setImageDrawable(resource);
}
複製代碼

3 總結

上文的解析,一套完整的流程就走下來了,可是glide強大之處遠不止於此,還有具體的緩存策略沒有講解,後續會繼續對glide進行分析。

4 參考資料

Android圖片加載框架最全解析(二),從源碼的角度理解Glide的執行流程

Glide github

相關文章
相關標籤/搜索