Glide 源码阅读笔记(五)

Glide 源码阅读笔记(五)

在第一篇文章中我简单介绍了 Glide 实例的创建过程,重点介绍了 Glide 的内存缓存实现和磁盘的缓存实现:Glide 源码阅读笔记(一)

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

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

第四篇文章中介绍了 Glide 如何加载内存缓存和磁盘缓存的资源。其中内存缓存又分为存活的内存缓存资源和被缓存的内存缓存资源;磁盘缓存分为 RESOURCE_CACHE 类型(有尺寸等参数作为 key)缓存与 DATA_CACHE 类型(从网络中加载的原始缓存数据,无尺寸等等信息):Glide 源码阅读笔记(四)

本篇文章继续接着上篇文章中介绍没有磁盘缓存和内存缓存时,如何加载网络中的数据,以及如何写入 RESOURCE_CACHEDATA_CACHE 磁盘缓存。另外再讲讲加载任务对 Android 中生命周期的响应。

加载网络数据和写入磁盘缓存

在前面的文章中介绍过加载缓存时会工作在 DiskCacheExecutor 中执行(也就是 ResourceCacheGeneratorDataCacheGenerator),而加载网络数据时会在 ActiveSourceExecutor 中执行(SourceGenerator)。所以我们就以 SourceGenerator#startNext() 方法开始分析:

Java 复制代码
  @Override
  public boolean startNext() {
    // ...
    while (!started && hasNextModelLoader()) {
      // 找到一个可用的 DataLoader 中的 LoadData 对象
      loadData = helper.getLoadData().get(loadDataListIndex++);
      // 然后通过 LoadData 中最后的输出对象的 Class 对象,然后去查找 Decoder 和 Transcoder,他们都被封装在 LoadPath 中。
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 找到可以用的 LoadPath 对象。
        startNextLoad(loadData);
      }
    }
    return started;
  }

我删除了部分本次逻辑不会用到的代码,后面再继续看。这里首先去查找可以用的 DataLoader,然后获取到它的 LoadData 对象,然后获取到最后它处理后的对象的 Class 对象。我们这里其实就是找到一个网络请求相关的 DataLoader,我们输入的对象是 String 类型的地址,然后它的返回对象就是一个 InputStream

然后继续查找能够处理 InputStreamDecoderTranscoder,然后他们被封装在 LoadPath 对象中,然后调用 startNextLoad() 方法:

Java 复制代码
  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            // 回调 LoadData 加载网络数据成功。
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

就是一个简单的异步回调,成功后会调用 onDataReadyInternal() 方法,其实这个 data 就是 InputStream

Java 复制代码
  @Synthetic
  void onDataReadyInternal(LoadData<?> loadData, Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
  }

上面的代码分为两种情况,一种是允许 DataCache,一种是不允许,我们直接看允许缓存的逻辑(不允许缓存更加简单)。它会把 data 保存在 dataToCache 变量中,然后调用 cbreschedule() 方法,这个 cb 其实就是 DecodeJob,这个方法它会再次触发 SourceGenerator#startNext() 方法,这个时候我们就能继续看之前我们删除掉的那部分代码了:

Java 复制代码
  @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      try {
        // 执行缓存 DataCache 操作。
        boolean isDataInCache = cacheData(data);
        if (!isDataInCache) {
          return true;
        }
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to properly rewind or write data to cache", e);
        }
      }
    }
    
    // 缓存成功后会调用 DataCacheGenerator#startNext() 方法
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    // ...
    
    return started;
  }

由于回调成功后 dataToCache 变量就不为空了,然后通过 cacheData() 方法来缓存 DataCache 到本地磁盘。如果缓存成功,会创建一个 DataCacheGenerator (它是和 DecodeJob 中的 DataCacheGenerator 是不同的实例),然后调用其 startNext() 方法,简单来说就是缓存成功后再去从缓存中再去加载一次,然后继续后续的操作。

我们再来看看 cacheData() 方法的实现:

Java 复制代码
  private boolean cacheData(Object dataToCache) throws IOException {
    long startTime = LogTime.getLogTime();
    boolean isLoadingFromSourceData = false;
    try {
      // 用 DataRewinder 来封装对应的 data,使其可以重复的读(也就是让 InputStream 可以重复的读)
      DataRewinder<Object> rewinder = helper.getRewinder(dataToCache);
      // 重置 data。
      Object data = rewinder.rewindAndGet();
      // 查找对应的 Encoder
      Encoder<Object> encoder = helper.getSourceEncoder(data);
      // 构建用于写入文件缓存的 DataCacheWriter 对象。
      DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, data, helper.getOptions());
      // 构建用于缓存的 DataCacheKey
      DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      DiskCache diskCache = helper.getDiskCache();
      // 写入缓存
      diskCache.put(newOriginalKey, writer);
      
      // 写入后,判断是否已经写入成功
      if (diskCache.get(newOriginalKey) != null) {
        // 写入成功后构建一个 DataCacheGenerator 对象
        originalKey = newOriginalKey;
        sourceCacheGenerator =
            new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
        // We were able to write the data to cache.
        return true;
      } else {

        isLoadingFromSourceData = true;
        // 如果缓存写入失败,就直接回调 DecodeJob。 
        cb.onDataFetcherReady(
            loadData.sourceKey,
            rewinder.rewindAndGet(),
            loadData.fetcher,
            loadData.fetcher.getDataSource(),
            loadData.sourceKey);
      }
      // We failed to write the data to cache.
      return false;
    } finally {
      if (!isLoadingFromSourceData) {
        loadData.fetcher.cleanup();
      }
    }
  }

上面代码的注释我写的很清楚了,就不再赘述了,如果写入本地缓存成功,会创建 DataCacheGenerator,前面我们说到会调用它的 startNext() 方法,也就是会触发 DataCacheGenerator 去加载刚才写入的缓存,DataCacheGenerator 我在上篇文章中已经介绍过了,不知道的同学翻翻前面的文章。

加载成功后会调用 SourceGenerator#onDataFetcherReady() 方法:

Java 复制代码
  @Override
  public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
  }

然后继续回调 DecodeJob#onDataFetcherReady() 方法:

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) {
      reschedule(RunReason.DECODE_DATA);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

上面的方法处理也非常简单将回调的各种数据保存在成员变量中,我们这里没有切线程,所以接着调用 decodeFromRetrievedData()

Java 复制代码
  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() 方法。

Java 复制代码
  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() 方法。

Java 复制代码
  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);
  }

获取到对应的 LoadPath,然后继续调用 runLoadPath() 方法。

Java 复制代码
  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 {
      return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }

这里继续调用 LoadPath#load() 来完成解码。这里其实有两个过程,一个是 Decoder 的解码过程,然后是 TranscoderDecoder 的结果转换成 Target 能够渲染的类型。其中 DecodeCallback 能够拦截处理 Decoder 返回的结果。

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

我们看到又调用了 DecodeJob#onResourceDecoded() 方法。这个方法里面就要来处理 RESOURCE_DISK_CACHE 了。

Java 复制代码
  @Synthetic
  @NonNull
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
    @SuppressWarnings("unchecked")
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      // 通过 Transformation 对尺寸和 ScaleType 的处理。
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    // TODO: Make this the responsibility of the Transformation.
    if (!decoded.equals(transformed)) {
      decoded.recycle();
    }

    final EncodeStrategy encodeStrategy;
    final ResourceEncoder<Z> encoder;
    // 判断是否允许 Resource Encoder
    if (decodeHelper.isResourceEncoderAvailable(transformed)) {
      // 查找对应的 Encoder
      encoder = decodeHelper.getResultEncoder(transformed);
      // 获取对应的 EncodeStrategy
      encodeStrategy = encoder.getEncodeStrategy(options);
    } else {
      encoder = null;
      encodeStrategy = EncodeStrategy.NONE;
    }

    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    // 判断是否允许 Resource Cache.
    if (diskCacheStrategy.isResourceCacheable(
        isFromAlternateCacheKey, dataSource, encodeStrategy)) {
      if (encoder == null) {
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
      }
      final Key key;
      // 根据 EncodeStrategy 来选择不同的缓存 Key
      switch (encodeStrategy) {
        case SOURCE:
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          key =
              new ResourceCacheKey(
                  decodeHelper.getArrayPool(),
                  currentSourceKey,
                  signature,
                  width,
                  height,
                  appliedTransformation,
                  resourceSubClass,
                  options);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }

      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      // 初始化 deferredEncodeManager。
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    return result;
  }

上面有一个点需要注意就是如果数据来源是 RESOURCE_DISK_CACHE 那么就不需要裁剪操作,因为 RESOURCE_DISK_CACHE 缓存中的数据已经做过裁剪操作了。如果需要处理 RESOURCE_DISK_CACHE 那么就会初始化 deferredEncodeManager

我们再来看看解码完成后 notifyEncodeAndRelease() 方法是怎么处理的:

Java 复制代码
  private void notifyEncodeAndRelease(
      Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
    GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
    try {
      // ...
      try {
        if (deferredEncodeManager.hasResourceToEncode()) {
          deferredEncodeManager.encode(diskCacheProvider, options);
        }
      } finally {
        if (lockedResource != null) {
          lockedResource.unlock();
        }
      }
      // ...
    } finally {
      GlideTrace.endSection();
    }
  }

我们看到它会调用 DeferredEncodeManager#encode() 方法:

Java 复制代码
    void encode(DiskCacheProvider diskCacheProvider, Options options) {
      GlideTrace.beginSection("DecodeJob.encode");
      try {
        diskCacheProvider
            .getDiskCache()
            .put(key, new DataCacheWriter<>(encoder, toEncode, options));
      } finally {
        toEncode.unlock();
        GlideTrace.endSection();
      }
    }

也没什么好说的了,直接就写入到磁盘缓存中了。

我们再看看缓存策略的接口:

Java 复制代码
  // 是否可以写入 DataCache 缓存
  public abstract boolean isDataCacheable(DataSource dataSource);

  // 是否可以写入 ResourceCache 缓存
  public abstract boolean isResourceCacheable(
      boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy);

  // 是否可以读取 ResourceCache 缓存
  public abstract boolean decodeCachedResource();

  // 是否可以读取 DataCache 缓存
  public abstract boolean decodeCachedData();

Glide 实现了以下几种:

  • AUTOMATIC (默认策略)
Java 复制代码
  public static final DiskCacheStrategy AUTOMATIC =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource == DataSource.REMOTE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
                  || dataSource == DataSource.LOCAL)
              && encodeStrategy == EncodeStrategy.TRANSFORMED;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };
  • ALL
Java 复制代码
  public static final DiskCacheStrategy ALL =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource == DataSource.REMOTE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return dataSource != DataSource.RESOURCE_DISK_CACHE
              && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };
  • NONE
Java 复制代码
  public static final DiskCacheStrategy NONE =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return false;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return false;
        }

        @Override
        public boolean decodeCachedResource() {
          return false;
        }

        @Override
        public boolean decodeCachedData() {
          return false;
        }
      };
  • DATA
Java 复制代码
  public static final DiskCacheStrategy DATA =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource != DataSource.DATA_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return false;
        }

        @Override
        public boolean decodeCachedResource() {
          return false;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };
  • RESOURCE
Java 复制代码
  public static final DiskCacheStrategy RESOURCE =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return false;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return dataSource != DataSource.RESOURCE_DISK_CACHE
              && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return false;
        }
      };

生命周期对 Request 的影响

在第二篇文章中我介绍了 GlideAndroid 生命周期的处理,Glide 会监听 FramgentActivityFragmentonStart()onStop()onDestroy() 三个生命周期。这些生命周期时间会下发至 RequestManager,然后 RequestManager 再去控制它的 Request

onStart()

onStart() 生命周期中主要是重新开始 onStop() 生命周期中被暂停的 Request。我们直接看看 RequestManager#onStart() 方法源码:

Java 复制代码
  @Override
  public synchronized void onStart() {
    // 恢复暂停的 Request。
    resumeRequests();
    // 回调 Target 的 onStart() 方法。
    targetTracker.onStart();
  }

我们首先看看 resumeRequests() 方法中是如何暂停 Request 的:

Java 复制代码
  public synchronized void resumeRequests() {
    requestTracker.resumeRequests();
  }

继续追踪 RequestTracker#resumeRequests() 方法的实现:

Java 复制代码
  public void resumeRequests() {
    // 更新暂停状态
    isPaused = false;
    // 遍历所有的 Request
    for (Request request : Util.getSnapshot(requests)) {
      // 如果 Request 没有在 Running 状态,调用对应的 begin() 方法
      if (!request.isComplete() && !request.isRunning()) {
        request.begin();
      }
    }
    // 清除暂停的 Request 的列表
    pendingRequests.clear();
  }

上面代码非常简单,遍历所有的暂停的 Request 然后调用其对应的 begin() 方法恢复请求。

我们再来看看 TargetTracker#onStart() 方法的源码:

Java 复制代码
  @Override
  public void onStart() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onStart();
    }
  }

朴实无华的代码,遍历所有的 Target 然后调用其 onStart() 方法。

我们来看看 ImageViewTarget#onStart() 方法是如何处理的:

Java 复制代码
  @Override
  public void onStart() {
    if (animatable != null) {
      animatable.start();
    }
  }

其实也就是恢复没有执行完成的动画。

onStop()

onStop() 生命周期中会暂停没有完成的 Request,虽然说是暂停,其实并不是真正的暂停,而且网络调用也并不能暂停,Glide 中所谓的暂停,其实就是暂停处理回调成功后的结果,等到下次 onStart() 后再继续处理。我们继续看看 RequestManager#onStop() 的实现:

Java 复制代码
  @Override
  public synchronized void onStop() {
    // 调用 Target 的 onStop() 生命周期
    targetTracker.onStop();
    if (clearOnStop) {
      // 清除没有完成的 Request
      clearRequests();
    } else {
      // 暂停没有完成的 Request
      pauseRequests();
    }
  }

首先通过 TargetTracker#onStop() 方法通知 TargetonStop() 生命周期;后续的代码分为两种情况:通过 pauseRequests() 方法暂停没有完成的 Request(这也是默认的处理方式);通过 clearRequests() 方法清除没有完成的 Request(我们讲 onDestroy() 的时候再讲这个方法)。

我们看看 ImageViewTarget#onStop() 怎么处理 onStop() 生命周期的:

Java 复制代码
  @Override
  public void onStop() {
    if (animatable != null) {
      animatable.stop();
    }
  }

onStart() 生命周期对应,就是停止正在执行的动画。

继续看看 pauseRequests() 方法是如何暂停 Request 的:

Java 复制代码
public synchronized void pauseRequests() {  
   requestTracker.pauseRequests();  
}

继续追踪 RequestTracker#pauseRequsts() 方法:

Java 复制代码
  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      if (request.isRunning()) {
        request.pause();
        pendingRequests.add(request);
      }
    }
  }

遍历所有的 Request,如果正在运行,那么调用 Request#pause() 方法,同时将对应的 Request 添加到 pendingRequests 列表中。

我们在前面的文章中知道,Request 真正的实现类是 SingleRequest,我们来看看 SingleRequest#pause() 是怎么实现的:

Java 复制代码
  @Override
  public void pause() {
    synchronized (requestLock) {
      if (isRunning()) {
        clear();
      }
    }
  }

继续调用对应的 clear() 方法:

Java 复制代码
  @Override
  public void clear() {
    Resource<R> toRelease = null;
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      if (status == Status.CLEARED) {
        return;
      }
      cancel();
      // Resource must be released before canNotifyStatusChanged is called.
      if (resource != null) {
        toRelease = resource;
        resource = null;
      }
      if (canNotifyCleared()) {
        target.onLoadCleared(getPlaceholderDrawable());
      }

      GlideTrace.endSectionAsync(TAG, cookie);
      status = Status.CLEARED;
    }

    if (toRelease != null) {
      engine.release(toRelease);
    }
  }

上面代码会继续调用 cancel() 方法,然后将状态更新为 CLEARED,还可能通知 Target#onLoadCleared() 方法,这里的参数用的是 PlaceholderDrawable()。如果当前的 Resource 不为空,还会将其释放。

我们来看看 cancel() 方法的实现:

Java 复制代码
  private void cancel() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    target.removeCallback(this);
    if (loadStatus != null) {
      loadStatus.cancel();
      loadStatus = null;
    }
  }

首先将 TargetSize 回调从 Target 中移除,然后调用 LoadStatus#cancel() 方法。加载 TargetSize 在第四篇文章中说过,这个 LoadStatusEngine#load() 方法返回的,用它来控制加载过程中任务,在第四篇文章中也说过。

Java 复制代码
    public void cancel() {
      synchronized (Engine.this) {
        engineJob.removeCallback(cb);
      }
    }

其实就是简单将请求成功/失败的回调从 EngineJob 中移除,可能你忘记了这个 Callback 是什么时候添加进去的,在第四篇文章中讲过,我们再回忆回忆 SingleRequest#onSizeReady() 方法:

Java 复制代码
  @Override
  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);

      // ...
    }
  }

上面 Engine#load() 方法传递的 this 其实就是上面被移除的 Callback。所以 Glide 中的暂停其实就是将 SingleRequest 中的 Callback 移除,这会影响后续的 Target 的渲染相关的 UI 流程,但是并不会影响 Engine 中对内存缓存,磁盘缓存的逻辑处理,所以当下次 SingleRequest 恢复时,它就能够直接从内存缓存中或者磁盘缓存中快速加载,而不用再请求网络。

onDestroy()

onDestroy() 也表明 RequestManager 到了生命末期,它和它其中的 RequestTarget 也都需要被销毁而且都不能够再恢复。

Java 复制代码
  @Override
  public synchronized void onDestroy() {
    // 对 Target 回调 onDestroy 生命周期
    targetTracker.onDestroy();
    // 移除 Target 中引用的 Request
    clearRequests();
    // 清除所有的 Request
    requestTracker.clearRequests();
    // 移除 RequestManager 对生命周期的监听
    lifecycle.removeListener(this);
    // 移除 ConnectiveityMonitor 对生命周期的监听
    lifecycle.removeListener(connectivityMonitor);
    Util.removeCallbacksOnUiThread(addSelfToLifecycle);
    // 通知 Glide 当前 RquestManager 被移除。
    glide.unregisterRequestManager(this);
  }

ImageViewTarget 中是把 Request 存放在 ImageViewTag 中,前面有说到过,我这里再啰嗦下。然后我们再看看 RequestTracker#clearRequests() 方法是如何清除 Request 的(注意区分和 pauseRequests() 方法处理的方式):

Java 复制代码
  public void clearRequests() {
    for (Request request : Util.getSnapshot(requests)) {
      // It's unsafe to recycle the Request here because we don't know who might else have a
      // reference to it.
      clearAndRemove(request);
    }
    pendingRequests.clear();
  }

遍历所有的 Request 并调用 clearAndRemove() 方法,同时清空 pendingRequests 中暂停的 Request

我们看看 clearAndRemove() 方法的实现:

Java 复制代码
  public boolean clearAndRemove(@Nullable Request request) {
    if (request == null) {
      // If the Request is null, the request is already cleared and we don't need to search further
      // for its owner.
      return true;
    }
    boolean isOwnedByUs = requests.remove(request);
    // Avoid short circuiting.
    isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
    if (isOwnedByUs) {
      request.clear();
    }
    return isOwnedByUs;
  }

上面会将所有 RequestrequestspendingRequests 中移除,同时调用 Request#clear() 方法(这个方法我们上面已经分析过了),而 resumeRequests() 方法中并不会将 Requestreuqests 中移除,而且会将 Request 添加到 pendingRequests 中去,供下次恢复时再调用对应的 begin() 方法。

最后

本篇文章介绍了 Glide 如何加载网络请求的数据和如何写入磁盘缓存(包括 ResourceCacheDataCache );还介绍了 RequestManager 具体如何处理不同的 Android 生命周期。

相关推荐
桦说编程28 分钟前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
阿巴斯甜9 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker9 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952710 小时前
Andorid Google 登录接入文档
android
黄林晴12 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android