Glide 源码阅览笔记(四)

在榜首篇文章中我简略介绍了Glide实例的创建进程,重点介绍了Glide的内存缓存完成和磁盘的缓存完成:Glide 源码阅览笔记(一)

在第二篇文章中介绍了Glide对生命周期的管理:Glide 源码阅览笔记(二)

在第三篇文章中介绍了 RequestRequestCoordinator,还简略介绍了 Registry 中的中心组件:Glide 源码阅览笔记(三)

今日的内容首要是介绍 SingleRequest 是怎样处理一个图片加载的使命。

源码阅览根据Glide 4.16.0版本

SingleRequest

咱们在前面的文章中有提到真实完成图片使命加载的是 SingleRequest,咱们就以 SingleRequest#begin() 办法作为开端来剖析:

  @Override
  public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
      // 怎样 model 为空,直接回调失利,一起回来办法
      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;
      }
      // 假如当时的状况是 RUNNING,直接抛出反常
      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }
      // 假如当时的状况是 COMPLETE,直接回调成功,一起回来办法
      if (status == Status.COMPLETE) {
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      }
      experimentalNotifyRequestStarted(model);
      cookie = GlideTrace.beginSectionAsync(TAG);
      // 更新状况
      status = Status.WAITING_FOR_SIZE;
      // 假如当时的尺度合法
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        // 告诉尺度现已准备好
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        // 等候 target (ImageView)完成尺度的丈量。
        target.getSize(this);
      }
      // 回调加载开始
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }
  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,
  }

这儿简略阐明一下上面的代码:

  1. 怎样 model (加载的 url 就属于 model) 为空,直接回调失利,一起回来办法。
  2. 假如当时的状况是 RUNNING,直接抛出反常。
  3. 假如当时的状况是 COMPLETE,直接回调成功,一起回来办法。
  4. 检查当时的 withheight 是否合法,假如合法就直接调用 onSizeReady() 办法,假如不合法需求等候 target 完成丈量,也便是等候 ImageView 完成丈量。
  5. 最终回调加载开始。

咱们来看看 ViewTarget#getSize() 是怎样获取 View 尺度的。

  @CallSuper
  @Override
  public void getSize(@NonNull SizeReadyCallback cb) {
    sizeDeterminer.getSize(cb);
  }

持续调用 SizeDeterminer#getSize() 办法。

   void getSize(@NonNull SizeReadyCallback cb) {
      // 获取当时 View 的尺度
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      // 假如当时 View 的尺度合法,直接回调成功
      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
        return;
      }
      // 添加到监听中
      if (!cbs.contains(cb)) {
        cbs.add(cb);
      }
      if (layoutListener == null) {
        // 在 ViewTreeObserver 中添加 PreDraw 时的监听。
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        observer.addOnPreDrawListener(layoutListener);
      }
    }
    private int getTargetHeight() {
      int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
      LayoutParams layoutParams = view.getLayoutParams();
      int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
      return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
    }
    private int getTargetWidth() {
      int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
      LayoutParams layoutParams = view.getLayoutParams();
      int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
      return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
    }
    private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
      int adjustedParamSize = paramSize - paddingSize;
      if (adjustedParamSize > 0) {
        return adjustedParamSize;
      }
      if (waitForLayout && view.isLayoutRequested()) {
        return PENDING_SIZE;
      }
      int adjustedViewSize = viewSize - paddingSize;
      if (adjustedViewSize > 0) {
        return adjustedViewSize;
      }
      if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
        if (Log.isLoggable(TAG, Log.INFO)) {
          // ...
        }
        return getMaxDisplayLength(view.getContext());
      }
      return PENDING_SIZE;
    }

获取 View 的尺度在咱们的实践的开发中也是经常遇到的,咱们来看看 Glide 是怎样做的,它给咱们写了一个十分好的样板代码。

  1. 获取当时的 View 的尺度,并判别尺度是否合法(其实便是大于 0 就合法),他获取当时尺度的方式有两种,首要从 ViewLayoutParams 中获取尺度,假如这个尺度合法,就直接运用,假如 LayoutParams 中的尺度不合法,就从 View 中去获取,核算可用的尺度时都会减去 Padding 值。

  2. 假如当时 View 的尺度不可用,在 ViewTreeObserver 中添加一个 OnPreDrawListener 去监听,它的完成类是 SizeDeterminerLayoutListener

咱们持续看看 SizeDeterminerLayoutListener 的代码:

    private static final class SizeDeterminerLayoutListener
        implements ViewTreeObserver.OnPreDrawListener {
      private final WeakReference<SizeDeterminer> sizeDeterminerRef;
      SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
        sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
      }
      @Override
      public boolean onPreDraw() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "OnGlobalLayoutListener called attachStateListener=" + this);
        }
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if (sizeDeterminer != null) {
          sizeDeterminer.checkCurrentDimens();
        }
        return true;
      }
    }

留意这儿它持有的 SizeDeterminer 是弱引证,防止内存泄漏。当 onPreDraw() 办法回调时,会调用 SizeDeterminer#checkCurrentDimens() 办法,咱们看看这个办法的完成:

    @Synthetic
    void checkCurrentDimens() {
      if (cbs.isEmpty()) {
        return;
      }
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
        return;
      }
      notifyCbs(currentWidth, currentHeight);
      clearCallbacksAndListener();
    }

先获取 View 的尺度,同样先是获取 LayoutParams 中的,假如获取失利再去直接拿 View 中的,假如获取到的尺度是可用的,告诉每个 Callback,然后移除一切的 Callback

咱们再看看 SingleRequest 中的 onSizeReady() 办法是怎样处理的:

  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      if (IS_VERBOSE_LOGGABLE) {
        logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;
      float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
      }
      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;
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

上面的代码比较简略,便是直接调用 Engine#load() 办法,其间回来值 loadStatus 可以用来控制加载的使命。

Engine

咱们持续看看 Engine#load() 办法中详细做了什么事情:

  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;
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);
    EngineResource<?> memoryResource;
    synchronized (this) {
      // 先测验从内存缓存中加载
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
      if (memoryResource == null) {
        // 内存缓存中过加载失利,再去从磁盘缓存或者网络中去加载
        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;
  }

上面代码比较简略,经过 loadFromMemory() 去加载内存中的缓存,假如内存中的缓存为空,再经过 waitForExistingOrStartNewJob() 办法去加载磁盘中的缓存或者经过网络去加载。

加载内存缓存

咱们先看看 loadFromMemory() 办法是怎样加载内存中的缓存的:

  @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);
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }
    return null;
  }

内存中的缓存分为两种,存活的资源和缓存中的资源。你或许有点懵,我来解释一下。

  • 存活的资源
    存活的资源其实便是别的地方正在运用的资源,例如 ImageViewA 加载了一个资源 X,而且现已显现在屏幕上了,这个时分 ImageViewB 也去加载资源 X,这个时分就不需求再去加载,直接复用 ImageViewA 加载的资源 X 就好了。
  • 缓存中的资源
    这个便是内存缓存的资源,咱们在榜首篇文章中就有介绍内存的缓存类,它的完成类是 LruResourceCache,缓存的巨细是 2 倍屏幕巨细图片所占用的内存,假如忘记了的同学可以看看我的榜首篇文章。

咱们来看看 loadFromActiveResources() 办法是怎样加载存活的资源的:

  @Nullable
  private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }

经过 ActivityResources#get() 办法来获取资源的,资源用 EngineResource 来封装,获取到的资源然后回调用 EngineResource#acquire() 办法,表明添加一次它的引证。
咱们来看看 ActivityResources#get() 办法的完成:

  @VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  @Nullable
  synchronized EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }
    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }

它是直接储存在 HashMap 中,Value 是用 ResourceWeakReference 封装,他继承于 WeakReference,也便是咱们常说的弱引证。

ActivityResources#activate() 办法是用来添加资源目标,而 ActivityResources#deactivate() 办法便是用来收回资源目标,咱们来看看他们的完成:

  synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }
  synchronized void deactivate(Key key) {
    ResourceWeakReference removed = activeEngineResources.remove(key);
    if (removed != null) {
      removed.reset();
    }
  }

上面的代码比较简略我就不再阐明晰,不过这儿还有一个要害点,便是在构建 ResourceWeakReference 弱引证时添加了一个引证行列 resourceReferenceQueue,被移除了的弱引证会被添加到这个引证行列中,不清楚的同学可以去查找一下相关的资料。

在结构函数中会在一个线程池中监听 resourceReferenceQueue 的状况:

  @VisibleForTesting
  ActiveResources(
      boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
    this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
    this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
    monitorClearedResourcesExecutor.execute(
        new Runnable() {
          @Override
          public void run() {
            cleanReferenceQueue();
          }
        });
  }

咱们再来看看 cleanReferenceQueue() 办法的完成:

  @Synthetic
  void cleanReferenceQueue() {
    while (!isShutdown) {
      try {
        ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
        cleanupActiveReference(ref);
        // This section for testing only.
        DequeuedResourceCallback current = cb;
        if (current != null) {
          current.onResourceDequeued();
        }
        // End for testing only.
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
  }

其实便是监控被移除的 ResourceWeakReference,然后再做一些整理作业。

到这儿 ActivityResources 咱们就介绍完了。前面咱们提到 EngineResource 会调用 acquire() 办法来添加引证次数。咱们来看看它:

  synchronized void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    ++acquired;
  }

直接把 acquired 变量加 1。咱们再看看减少引证次数的办法 release() 的完成:

  void release() {
    boolean release = false;
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) {
        release = true;
      }
    }
    if (release) {
      listener.onResourceReleased(key, this);
    }
  }

直接把 acquired 变量减 1,假如 acquired 变成 0 了就表明没有引证了,那么就会调用 listener.onResourceReleased() 办法,这个 listener 其实便是 Engine,咱们持续看看 Engine#onResourceReleased() 办法怎样处理:

  @Override
  public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource, /* forceNextFrame= */ false);
    }
  }

上面代码简略,假如答应运用内存缓存,直接缓存到 LruResourceCache 中,假如不答应缓存直接经过 ResourceRecycler#recycle() 办法收回。

到这儿存活的资源的管理方式就介绍完了。

咱们持续看看 loadFromCache() 办法获取缓存中的资源:

  private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }
  private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);
    final EngineResource<?> result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      // Save an object allocation if we've cached an EngineResource (the typical case).
      result = (EngineResource<?>) cached;
    } else {
      result =
          new EngineResource<>(
              cached,
              /* isMemoryCacheable= */ true,
              /* isRecyclable= */ true,
              key,
              /* listener= */ this);
    }
    return result;
  }

上面的代码十分简略,直接从 LruResourceCache 获取就行了,一起会移除对应的缓存,假如获取到的缓存不是 EngineResource 类型,还会从头用 EngineResource 封装一下,获取到的缓存还会被添加到 ActiveResources 中,表明当时存活的资源。

加载磁盘中的缓存

咱们持续看看 Engine#load() 办法中调用的 waitForExistingOrStartNewJob() 是怎样作业的:

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

一切的使命都会被添加到 jobs 成员变量中,这儿会创建两个 Job,分别是 DecodeJobEngineJob,他们都互相持有对方的引证,EngineJob 首要担任线程调度和告诉上层 Engine 各种加载状况,而 DecodeJob 首要担任图片加载使命的处理。最终回来一个 LoadStatus 目标,SingleRequest 就可以经过这个目标来取消使命。

咱们看看 EngineJob#start() 办法的代码:

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

假如答应磁盘缓存,DecodeJob 就会被添加到 diskCacheExecutor 中履行。

咱们持续看看 DecodeJob#run() 办法:

  @Override
  public void run() {
    GlideTrace.beginSectionFormat("DecodeJob#run(reason=%s, model=%s)", runReason, model);
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (CallbackException e) {
      throw e;
    } catch (Throwable t) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(
            TAG,
            "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
            t);
      }
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      if (!isCancelled) {
        throw t;
      }
      throw t;
    } finally {
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  }

忽略各种错误处理和告诉,这儿会调用 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);
    }
  }
  /** Why we're being executed again. */
  private enum RunReason {
    /** The first time we've been submitted. */
    INITIALIZE,
    /** We want to switch from the disk cache service to the source executor. */
    SWITCH_TO_SOURCE_SERVICE,
    /**
     * We retrieved some data on a thread we don't own and want to switch back to our thread to
     * process the data.
     */
    DECODE_DATA,
  }

默许的 RunReasonINITIALIZE,所以首要会调用 getNextState(),然后调用 getNextGenerator(),最终调用 runGenerators(),分别看看上面的办法:

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        // 判别当时是否支撑 ResourceCache,假如支撑下一个状况便是 RESOURCE_CACHE,假如不支撑就从头核算
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE
            : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        // 判别是否支撑 DataCache,假如支撑下一个状况便是 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.
        // 判别是不是只能运用磁盘缓存,假如是下个状况便是 FINISHED,反之便是 SOURCE。
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

我信任许多人对 RESOUCE_CACHEDATA_CACHE 或许分不清,我这儿简略解释一下上面几个比较重要的状况。

  • RESOUCE_CACHE
    缓存处理过的原本的网络图片,比方图片的尺度等等优化过,也或许根据 ScaleType 裁剪过。
  • DATA_CACHE
    缓存未经过原本处理过的网络图片,也便是图片原本是什么姿态,缓存便是什么姿态。
  • SOURCE
    经过网络去获取图片资源。

Glide 默许的磁盘缓存策略是 AUTOMATIC,默许的网络图片是运用的 DATA_CACHE

咱们持续看 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);
    }
  }

上面代码十分简略,RESOURCE_CACHEResourceCacheGenerator 来处理;DATA_CACHEDataCacheGenerator 来处理;SOURCESourceGenerator 来处理。
咱们持续看看 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(RunReason.SWITCH_TO_SOURCE_SERVICE);
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }
  }

上面办法也是比较简略直接调用 Generator#startNext() 办法来履行对应的使命,假如办法回来 false 表明持续履行下一个状况的 Generator,最终直到履行完一切的 Generator,留意这儿假如状况是 SOURCE,也便是网络恳求的状况,会调用 reschedule() 办法,一起 RunReasonSWITCH_TO_SOURCE_SERVICE,其实便是网络恳求的 Generator 会在另外的线程履行。

看看 reschedule() 办法:

  private void reschedule(RunReason runReason) {
    this.runReason = runReason;
    callback.reschedule(this);
  }

这个 callback 其实便是 EngineJob,咱们再看看 EngineJob#reschedule()

@Override
public void reschedule(DecodeJob<?> job) {  
   getActiveSourceExecutor().execute(job);  
}

咱们看到这儿会切换至 SourceExecutor 持续履行后续的使命。

咱们来看看 ResourceCacheGenerator#startNext() 是怎样履行加载 RESOURCE_CACHE 缓存使命的:

  @Override
  public boolean startNext() {
    GlideTrace.beginSection("ResourceCacheGenerator.startNext");
    try {
      // 获取一切的支撑的 ModelLoader 中的 LoadData 目标的 Keys。
      List<Key> sourceIds = helper.getCacheKeys();
      if (sourceIds.isEmpty()) {
        return false;
      }
      // 获取一切的可用的 Transcoder 转化后的数据的 Class 目标(咱们的 demo 中是 Drawable)
      List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
      if (resourceClasses.isEmpty()) {
        if (File.class.equals(helper.getTranscodeClass())) {
          return false;
        }
        // 假如没有可用的 Transcoder 直接抛出反常。
        throw new IllegalStateException(
            "Failed to find any load path from "
                + helper.getModelClass()
                + " to "
                + helper.getTranscodeClass());
      }
      // 遍历一切的 ModelLoader 和 ResourceClasses 目标生成的 key,经过这个 key 去查找到一个可用的缓存文件对应的 ModelLoader。
      while (modelLoaders == null || !hasNextModelLoader()) {
        resourceClassIndex++;
        if (resourceClassIndex >= resourceClasses.size()) {
          sourceIdIndex++;
          if (sourceIdIndex >= sourceIds.size()) {
            return false;
          }
          resourceClassIndex = 0;
        }
        // 获取对应的 ModelLoader 中的 Key
        Key sourceId = sourceIds.get(sourceIdIndex);
        // 获取对应 Transcoder 输出的 class
        Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
        // 获取裁剪方式的 Transformation
        Transformation<?> transformation = helper.getTransformation(resourceClass);
        // 经过上面的参数生成各种 Key。
        currentKey =
            new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
                helper.getArrayPool(),
                sourceId,
                helper.getSignature(),
                helper.getWidth(),
                helper.getHeight(),
                transformation,
                resourceClass,
                helper.getOptions());
        // 经过生成的 Key 从 DiskLruCache 中去查找缓存文件.        
        cacheFile = helper.getDiskCache().get(currentKey);
        if (cacheFile != null) {
          // 缓存文件不为空,去查找可以处理 File 类型的 ModelLoaders。
          sourceKey = sourceId;
          modelLoaders = helper.getModelLoaders(cacheFile);
          modelLoaderIndex = 0;
        }
      }
      loadData = null;
      boolean started = false;
      // 遍历找到的一切的 ModelLoader,找到一个可用的去加载 File 缓存文件。
      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;
          经过 ModelLoader 中 loadData 中的 fetcher 去加载缓存的 File
          loadData.fetcher.loadData(helper.getPriority(), this);
        }
      }
      // 最终的回来成果表明是否拿到可用的缓存.
      return started;
    } finally {
      GlideTrace.endSection();
    }
  }

假如对 Registry 中的中心组件不熟悉的同学可以看看我前面的一篇文章,总的来说处理 model (url) 的流程便是:ModelLoader -> Decoder -> Transcoder

上面的代码稍微有点小小的复杂,总的来说便是遍历一切的可用的 ModelLoaderTrascoder 最终的输出 ResourceClass 目标生成的 Key,然后经过这个 Key 从本地缓存中去查找对应的文件,假如有查找到对应的文件,然后去查找处理 File 类型的 ModelLoader,然后经过 ModelLoader 去加载对应的 File

这儿得留意一下查找缓存文件所对应的 ResourceCacheKey,它是由 ModelLoader ( Url )、withheightResourceClassTrascoder 转化后的目标 Class)和 Signature。我这儿要单独阐明一下这个 Signature,除了别的参数外,咱们可以简略的理解为经过 model (url) 来生成缓存文件 Key 的,大多数情况下咱们开发中也是一个 url 对应的一个图片,不过这儿有破例,一个 url 或许对应多个图片,比方我有这样一个 url 表明今日美图: https://img.tans.com。也便是这个 url 会根据日期去回来不同的图片。假如是这样 Glide 默许的缓存机制就会出现问题,当我今日加载图片并缓存后,第二天还是会运用这个缓存,而不会去真实去恳求网络,这不是咱们期望看到的成果。所以咱们需求自定义一个 Signature 来生成组合的 Key,咱们就可以运用日期来作为 Signature,这样咱们就可以解决上面遇到的问题。

上面多说了一点题外的话,咱们接着看加载 FileModelLoader 加载成功后接着怎样处理,加载成功的回调办法是 ResourceCacheGenerator#onDataReady()

  @Override
  public void onDataReady(Object data) {
    cb.onDataFetcherReady(
        sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
  }

持续回调 DecodeJob#onDataFetcherReady() 办法:

  @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) {
      reschedule(RunReason.DECODE_DATA);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        // 缓存的代码履行这儿
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

后续的代码持续调用 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();
    }
  }

首要经过 decodeFromData() 办法来解码数据,假如解码成功,经过 notifyEncodeAndRelease() 告诉成功。

咱们来看看 decodeFromData() 办法:

  private <Data> Resource<R> decodeFromData(
      DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
    try {
      if (data == null) {
        return null;
      }
      long startTime = LogTime.getLogTime();
      Resource<R> result = decodeFromFetcher(data, dataSource);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded result " + result, startTime);
      }
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

接着往下调用 decodeFromFetcher() 办法:

  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    return runLoadPath(data, dataSource, path);
  }

上面的 DecodeHelper#getLoadPath() 办法便是查找到一个可以处理当时 dataLoadPath,它其间封装了对应的 DecoderTranscoder 等等要害组件,咱们持续追寻 runLoadPath() 这个办法:

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

上面的 DataRewinder 便是用来让咱们的 InputStream 可以重置,然后重复读取的处理目标。经过 LoadPath#load() 就可以获取到最终的成果了,这个他还添加了一个 DecodeCallback 回调。

    @NonNull
    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    }

这个时分会回调 DecodeJob#onResourceDecoded() 办法,这个办法中会处理磁盘缓存的写入。咱们讲网络加载的时分再介绍这个办法。

然后再看看 notifyEncodeAndRelease() 加载资源成功后的处理:

  private void notifyEncodeAndRelease(
      Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
    try {
      // ...
      notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey);
      // ...
    } finally {
      GlideTrace.endSection();
    }
  }

然后持续调用 notifyComplete() 办法,我注释掉了缓存相关的代码,后续讲网络的时分再看。

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

这个 callback 便是 EngineJob,咱们来看看它的 onResourceReady() 办法:

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

持续看看 notifyCallbacksOfResult() 办法:

  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
      } else if (hasResource) {
        throw new IllegalStateException("Already have resource");
      }
      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);
      localKey = key;
      localResource = engineResource;
    }
    engineJobListener.onEngineJobComplete(this, localKey, localResource);
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }

EngineJobListener 便是直接告诉 Engine,对应的 Entry 便是告诉 SingleRequest

咱们先来看看 Engine#onEngineJobComplete() 是怎样处理的:

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

这儿做两件事情,把当时的资源添加到 ActiveResources 中去,也便是存活的内存资源,前面讲过,然后把当时使命从 jobs 中移除。

咱们持续看看 SingleRequest#onResourceReady()

  @Override
  public void onResourceReady(
      Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
        if (resource == null) {
          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;
          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);
      }
    }
  }

代码都看吐了,处理很简略便是更新一下状况,然后持续向 parent 告诉状况。然后还会调用 Target#onResourceReady() 办法:

咱们看看 ImageViewTarget#onReousrceReady() 是怎样处理的:

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }

这其实就分两种情况,一种是有动画(所谓的动画也是不断修改 ImageView 中的 Drawable 罢了,我就不剖析了),一种是没有动画:

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

总算把加载 RESOUCE_CACHE 的逻辑讲完了,不过后续的 DATA_CACHE 的加载与 SOURCE 的加载许多的逻辑都是和这个是相同的,咱们只需求看看不同的地方就行了。

咱们再来看看 DataCacheGenerator#startNext() (DATA_CACHE) 怎样处理的:

  @Override
  public boolean startNext() {
    GlideTrace.beginSection("DataCacheGenerator.startNext");
    try {
      while (modelLoaders == null || !hasNextModelLoader()) {
        sourceIdIndex++;
        if (sourceIdIndex >= cacheKeys.size()) {
          return false;
        }
        Key sourceId = cacheKeys.get(sourceIdIndex);
        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
        Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
        cacheFile = helper.getDiskCache().get(originalKey);
        if (cacheFile != null) {
          this.sourceKey = sourceId;
          modelLoaders = helper.getModelLoaders(cacheFile);
          modelLoaderIndex = 0;
        }
      }
      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;
    } finally {
      GlideTrace.endSection();
    }
  }

简直和 ResourceCacheGenerator 中的处理方式一模相同,仅仅查找缓存文件的 KeyDataCacheKey,他只由 ModelLoaderSignature 构成。而没有了前面的 withheight 等等参数,这也是 RESOURCE_CACHEDATA_CACHE 的区别。

最终

Glide 的代码真的有点累,本篇文章就只看了内存缓存和磁盘缓存的逻辑都又写了这么多了,原本还要写 SOURCE 网络加载部分的,这样就只有留到下一篇文章再剖析了。