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 生命周期。

相关推荐
吃着火锅x唱着歌5 分钟前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley1 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei3 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng3 小时前
安卓多渠道apk配置不同签名
android
枫_feng4 小时前
AOSP开发环境配置
android·安卓
叶羽西4 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_171538856 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)7 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
mmsx7 小时前
android 登录界面编写
android·登录界面
姜毛毛-JYM7 小时前
【JetPack】Navigation知识点总结
android