Glide 4.11.0 圖片加載流程深入分析源碼

從Glide圖片加載流程深入分析源碼

不知道有沒有小伙伴跟我一樣,使用Glide已經(jīng)有好多年,平時看源碼比較零碎,對Glide的源碼只能算一知半解,面試遇到分析Glide的源碼,無從說起……

那今天就從0開始,對Glide源碼進(jìn)行一個整體的梳理,更進(jìn)一步的了解Glide的原理。

注意:此文章基于Glide 4.11.0版本

眾所周知,我們使用Gilde加載圖片時,最常用的一行代碼:

Glide.with(context).load(url).into(imageView)

這里就只先分析此行代碼的主線流程,省略了其他的配置方法。

1. Glide.with()

首先從Glide的with方法開始,它是我們最開始調(diào)用的方法,有多個重載:

with.png

無論是哪個重載方法,內(nèi)部都是return了一行 getRetriever().get() 代碼:

@NonNull
public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}

先調(diào)用 getRetriever 返回了一個 RequestManagerRetriever 對象,然后在調(diào)用 RequestManagerRetrieverget 方法返回一個RequestManager 對象

RequestManagerRetrieverget方法,也是有多個重載:

get.png

其中最大的區(qū)別就是在于context和非context的參數(shù)的方法,實現(xiàn)各不相同:

如果是非Context的參數(shù),比如 activity、fragment、view,其中fragmentview都可以找到一個所屬的Activity對象,那么最后就會調(diào)用到get(Activity activity),并內(nèi)部調(diào)用fragmentGet方法:

public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else if (activity instanceof FragmentActivity) {
    return get((FragmentActivity) activity);
  } else {
    assertNotDestroyed(activity);
    frameWaiter.registerSelf(activity);
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

fragmentGet方法中,創(chuàng)建了一個Fragment和RequestManager:

private RequestManager fragmentGet(
    @NonNull Context context,
    @NonNull android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    // ...
  }
  return requestManager;
}

這里稍微提一下RequestManagerFragment,getRequestManagerFragment創(chuàng)建的RequestManagerFragment是Glide用來進(jìn)行圖片加載的生命周期管理的,最終通過FragmentManager添加到Activity上,它本身什么都不展示,只是一個空的Fragment,僅僅是通過Fragment的生命周期來管理圖片加載的請求,避免內(nèi)存泄漏問題,可以看getRequestManagerFragment方法的實現(xiàn):

private RequestManagerFragment getRequestManagerFragment(
    @NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint) {
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

回到主線,我們到這里就從RequestManagerRetriever中獲得了一個RequestManager對象,從而就可以開始第二步動作:load

2. Glide.with().load()

當(dāng)我們獲得一個RequestManager后,就可以接著調(diào)用.load()的方法,現(xiàn)在的調(diào)用就就到了Glide.with().load(),可以看到load也是有多個重載方法,可以支持各種數(shù)據(jù)的load,包括file、string的url,uri等等……

load.png

load內(nèi)部是通過調(diào)用asDrawable方法,asDrawable再調(diào)用了as方法,創(chuàng)建了一個RequestBuilder并返回:

asDrawable()

public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

as()

public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
  return new RequestBuilder<>(glide, this, resourceClass, context);
}

Glide.with().load().into()

這就是我們調(diào)用load方法之后得到RequestBuilder的由來,得到RequestBuilder之后,按照邏輯順序調(diào)用RequestBuilderinto方法,也是主線邏輯里面最重要的部分:

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  // ...
  // 構(gòu)建Request請求
  Request request = buildRequest(target, targetListener, options, callbackExecutor);

  Request previous = target.getRequest();
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
  // ...
    return target;
  }

  requestManager.clear(target);
  target.setRequest(request);
  // 執(zhí)行加載請求
  requestManager.track(target, request);

  return target;
}

這里省略了不相關(guān)的代碼,into方法中,主要是構(gòu)建了一個Request對象,然后調(diào)用requestManager.track(target, request)開始加載圖片,繼續(xù)跟蹤到requestManager.track方法,位于RequestManager.java中:

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  targetTracker.track(target);
  requestTracker.runRequest(request);
}

可以看到request繼續(xù)傳遞到requestTracker.runRequest(request)中,可以繼續(xù)跟蹤看看:

public void runRequest(@NonNull Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    // ...
  }
}

RequestTracker.java類里面,runRequest調(diào)用了request.begain()方法

由于Request是一個接口,這里的request對象在構(gòu)建的時候其實是構(gòu)建了它的實現(xiàn)類SingleRequest,位于RequestBuilder.java:

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

那么在SingleRequestbegin方法里面,判斷語句有好幾個,千萬要迷路,這里的重點是onSizeReady

public void begin() {
  synchronized (requestLock) {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    // ...
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }
  // ...
  }
}

繼續(xù)跟蹤onSizeReady,里面使用engine對象調(diào)用了load方法:

public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  synchronized (requestLock) {
    // ...
    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);
    // ...
  }
}

繼續(xù)分析Engine的load方法,這里加載請求就揭開了Gilde的緩存機(jī)制:

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;

  // 構(gòu)建加載資源的key
  EngineKey key =
      keyFactory.buildKey(
          model,
          signature,
          width,
          height,
          transformations,
          resourceClass,
          transcodeClass,
          options);

  EngineResource<?> memoryResource;
  synchronized (this) {
    // 從內(nèi)存中加載
    memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

    if (memoryResource == null) {
    // 內(nèi)存中沒有就等待或者是開始新的加載
      return waitForExistingOrStartNewJob(
          glideContext,
          model,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          options,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache,
          cb,
          callbackExecutor,
          key,
          startTime);
    }
  }

  cb.onResourceReady(
      memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
  return null;
}

首先Glide是 loadFromMemory 從內(nèi)存中加載資源,這里的內(nèi)存細(xì)分一下,分別是 活動資源內(nèi)存緩存,Glide是優(yōu)先從活動資源中獲取資源,獲取不到再從內(nèi)存緩存中獲取,如果內(nèi)存緩存也獲取不到,loadFromMemory 就返回null:

private EngineResource<?> loadFromMemory(
    EngineKey key, boolean isMemoryCacheable, long startTime) {
  // 檢查是否從內(nèi)存緩存中讀取
  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;
  }

// 從活動緩存中沒有讀取到資源
// 從內(nèi)存緩存中讀取
  EngineResource<?> cached = loadFromCache(key);
  if (cached != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    // 從內(nèi)存緩存中讀取讀取到了資源
    return cached;
  }
  return null;
} 

這里從內(nèi)存緩存中讀取到了資源之后,還把資源加入到活動緩存,這樣做的目的是為了下一次獲取數(shù)據(jù)更快(先從活動資源獲取數(shù)據(jù)):

private EngineResource<?> loadFromCache(Key key) {
  EngineResource<?> cached = getEngineResourceFromCache(key);
  if (cached != null) {
    cached.acquire();
    activeResources.activate(key, cached);
  }
  return cached;
}

現(xiàn)在回過頭來看 waitForExistingOrStartNewJob 方法,這個方法是從 loadFromMemory 沒有加載到數(shù)據(jù)時才調(diào)用,waitForExistingOrStartNewJob 里面構(gòu)建了 engineJobdecodeJob 的對象,engineJob 對象用來啟動了一個 decodeJob ,engineJob 很簡單,就是維護(hù)了線程池,進(jìn)行線程的調(diào)度,這里在start 里面執(zhí)行了 decodeJob

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

由此看來,既然是線程調(diào)度,decodeJob 是被執(zhí)行的部分,那DecodeJob類肯定是實現(xiàn)了 Runnable 接口,并將主要的邏輯寫到了 run 方法里面,跟蹤代碼一看,果不其然:

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

避免干擾,我將run方法里面的注釋和不相關(guān)的邏輯給刪除掉了:

public void run() {
  // ...
  try {
    // ...
    // 主要代碼,我們繼續(xù)跟蹤這個方法
    runWrapped();
  } catch (CallbackException e) {
    throw e;
  } catch (Throwable t) {
    // ...
  } finally {
    // ...
  }
}

繼續(xù)跟蹤runWrapped方法:

private void runWrapped() {
  switch (runReason) {
    case INITIALIZE:
  // 首次初始化并獲取資源
      stage = getNextStage(Stage.INITIALIZE);
      currentGenerator = getNextGenerator();
      runGenerators();
      break;
    case SWITCH_TO_SOURCE_SERVICE:
  // 從磁盤緩存獲取不到數(shù)據(jù),重新獲取
      runGenerators();
      break;
    case DECODE_DATA:
  // 獲取資源成功,解碼數(shù)據(jù)
      decodeFromRetrievedData();
      break;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}

這里通過我的注釋應(yīng)該很容易理解這個switch語句了,至于獲取圖片成功后的 decodeFromRetrievedData,暫時停一下稍候分析,其他的兩個case語句,不管是 INITIALIZE 初始化還是 SWITCH_TO_SOURCE_SERVICE 從磁盤緩存獲取不到數(shù)據(jù)進(jìn)行重試,都是要調(diào)用 runGenerators 來獲取數(shù)據(jù),我們繼續(xù)跟蹤看看這個方法:

private void runGenerators() {
  //...
  // 條件語句中的 currentGenerator.startNext() 才是重點
  while (!isCancelled
      && currentGenerator != null
      && !(isStarted = currentGenerator.startNext())) {

    stage = getNextStage(stage);
    currentGenerator = getNextGenerator();

    if (stage == Stage.SOURCE) {
      reschedule();
      return;
    }
  }
  // ...
}

首先看while的條件語句:

  • !isCancelled :如果沒有取消請求
  • currentGenerator != null : currentGenerator對象不為空
  • !(isStarted = currentGenerator.startNext()) : currentGenerator.startNext()的執(zhí)行結(jié)果會賦值給 isStarted

前兩個不過多解釋,主要是第三個語句:如果isStarted的值為false,表示沒有執(zhí)行成功,就會執(zhí)行while語句體里面的內(nèi)容,while語句體里面就會獲取下一個 Stage,以及通過Stage 來獲取對應(yīng)的 Generator

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

根據(jù)兩個方法聯(lián)合分析,getNextGenerator 執(zhí)行結(jié)果:

  • 第一次返回 ResourceCacheGenerator
  • 第二次返回 DataCacheGenerator
  • 第三次的返回取決于 getNextStage 方法中 case DATA_CACHE返回 Stage.FINISHEDStage.SOURCE,getNextGenerator 方法就返回 SourceGenerator 或者是 null

那其實這三個Generator:ResourceCacheGeneratorDataCacheGenerator、SourceGenerator,都是實現(xiàn)自DataFetcherGenerator接口,目的是為了從不同的Generator中加載資源,如果加載成功,就將資源返回,如果加載不成功,就繼續(xù)找下一個Generator加載

不管是從哪個Generator加載數(shù)據(jù),都是通過剛才的while語句中的startNext來執(zhí)行,不同的Generator內(nèi)部實現(xiàn)不一樣,但是都大同小異,都是加載數(shù)據(jù),加載結(jié)果是通過一個叫做FetcherReadyCallback的接口進(jìn)行回調(diào),這里隨便看一個,DataCacheGeneratorstartNext

public boolean startNext() {
  // ...
  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
    loadData =
        modelLoader.buildLoadData(
            cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
    if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
      started = true;
  // 加載資源重點代碼
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

加載資源的重點代碼是:loadData.fetcher.loadData(helper.getPriority(), this)

loadData.fetcher 獲得的是一個DataFetcher,DataFetcher是獲取資源的接口,所以loadData具體的工作其實是交給它的實現(xiàn)類來完成,它的實現(xiàn)類有HttpUrlFetcherLocalUriFetcher、FileFetcherAssetPathFetcher等等,用于支持各種資源的獲取。

至于加載的細(xì)節(jié),這里舉兩個例子:

  1. 從http url加載
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));
    }
  }
}
  1. 從文件加載
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
  try {
    data = opener.open(file);
    callback.onDataReady(data);
  } catch (FileNotFoundException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Failed to open file", e);
    }
    callback.onLoadFailed(e);
  }
}

加載成功,通過callback.onDataReady(data)進(jìn)行回調(diào),加載不成功,則通過callback.onLoadFailed(e)進(jìn)行回調(diào)。

以上就是Glide加載圖片的主要流程代碼分析,因為是省略了大部分的源碼和注釋,只保留了主線代碼,所以還是要自己去跟蹤一遍,才更能理解其中的原理,謝謝觀看!

最后編輯于
?著作權(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)容