Glide加载流程3——数据处理

580 阅读4分钟

DecodeJob实现了DataFetcherGenerator.FetcherReadyCallback接口:

  interface FetcherReadyCallback {
    //Glide重新调度,在Glide自己的线程中继续流程
    void reschedule();
	//数据获取成功
    void onDataFetcherReady(
        Key sourceKey,
        @Nullable Object data,
        DataFetcher<?> fetcher,
        DataSource dataSource,
        Key attemptedKey);
	//数据获取失败
    void onDataFetcherFailed(
        Key attemptedKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource);
  }

onDataFetcherFailed数据获取失败的处理逻辑:

public void onDataFetcherFailed(
      Key attemptedKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource) {
  fetcher.cleanup();
  GlideException exception = new GlideException("Fetching data failed", e);
  exception.setLoggingDetails(attemptedKey, dataSource, fetcher.getDataClass());
  throwables.add(exception);
  //线程不一样,修改runReason,重新调度,最终调用的还是runGenerators()
  if (Thread.currentThread() != currentThread) {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  } else {
    runGenerators();
  }
}

因此,如果数据获取失败,会继续调用runGenerators(),逻辑如下:

 private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
           //当前的generator开始加载数据
        && !(isStarted = currentGenerator.startNext())) {
      //下一个状态
      stage = getNextStage(stage);
      //根据状态,获取数据加载器
      currentGenerator = getNextGenerator();
	  //重新调度,切换到Glide自己的线程
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

  }
  1. 继续调用当前的DataFetcherGenerator.startNext(),因为ResourceCacheGenerator和DataCacheGenerator中都有序号计数,之前已尝试的ModelLoader.LoadData不会重复调用,没有找到合适的ModelLoader.LoadData,就会返回false

  2. 调用getNextStage()获取下一个状态:

      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);
        }
      }
    
  3. 如果RESOURCE_CACHE和DATA_CACHE都没找到,则会调用SourceGenerator

  4. 如果SOURCE也没有,那么请求失败,并且通知出去,notifyFailed()

reschedule重新调度:

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

重新调度只会发生在加载数据的阶段,所以设置runReason为RunReason.SWITCH_TO_SOURCE_SERVICE,并且调用callback的reschedule方法,而这个callback是EngineJob实现的(DecodeJob的创建逻辑在Engine中,且用到了对象池Pools.Pool)。

EngineJob的reschedule方法:

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

所以还是把DecodeJob丢到线程池里再次执行,最后还是调用了DecodeJob的run方法,判断状态后,调用了runWrapped方法。因为runReason的值,所以最终还是调用了runGenerators方法,用来加载数据。

onDataFetcherReady数据加载成功:

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;
  if (Thread.currentThread() != currentThread) {
      //重新放到线程池里执行,执行run->runWrapped->decodeFromRetrievedData
    runReason = RunReason.DECODE_DATA;
    callback.reschedule(this);
  } else {
    GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
    try {
      decodeFromRetrievedData();
    } finally {
      GlideTrace.endSection();
    }
  }
}

不管是reschedule重新调度,还是直接处理,最终都是调用了decodeFromRetrievedData方法进行解码工作:

 private void decodeFromRetrievedData() {
    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);
    } else {
      //解码失败,重新加载数据,流程在上文已分析
      runGenerators();
    }
  }
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();
  }
}

private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
    throws GlideException {
    //根据数据类型获取解码器,如HttpUrlFetcher的结果是InputStream
  LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
  return runLoadPath(data, dataSource, path);
}

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来做,解码的结果通过DecodeCallback这个内部类的回调,把结果设置给了dataSource这边变量,并最终调用了DecodeJob的onResourceDecoded方法。

解码完成1
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
  Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
  Transformation<Z> appliedTransformation = null;   
  Resource<Z> transformed = decoded;
    //如果不是已转换的数据缓存,则先使用设置的Transformation
  if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
    appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
    transformed = appliedTransformation.transform(glideContext, decoded, width, height);
  }
  //直接回收数据节省内存,或者放到缓存中供下次使用
  if (!decoded.equals(transformed)) {
    decoded.recycle();
  }

  final EncodeStrategy encodeStrategy;
  final ResourceEncoder<Z> encoder;
  if (decodeHelper.isResourceEncoderAvailable(transformed)) {
    encoder = decodeHelper.getResultEncoder(transformed);
    encodeStrategy = encoder.getEncodeStrategy(options);
  } else {
    encoder = null;
    encodeStrategy = EncodeStrategy.NONE;
  }

  Resource<Z> result = transformed;
  boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
  //缓存已转换的数据,因为此时的数据已解码,所以缓存到文件中,需要再次编码
  if (diskCacheStrategy.isResourceCacheable(
      isFromAlternateCacheKey, dataSource, encodeStrategy)) {
    if (encoder == null) {
      throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
    }
    final Key key;
    switch (encodeStrategy) {
      //Gif,定义在GifDrawableEncoder中
      case SOURCE:
        key = new DataCacheKey(currentSourceKey, signature);
        break;
      case TRANSFORMED:
      //Bitmap,定义在BitmapEncoder中
        key =
            new ResourceCacheKey(
                decodeHelper.getArrayPool(),
                currentSourceKey,
                signature,
                width,
                height,
                appliedTransformation,
                resourceSubClass,
                options);
        break;
      default:
        throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
    }
	//参数保存到deferredEncodeManager中,在下一步中进行encode
    LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
    deferredEncodeManager.init(key, encoder, lockedResult);
    result = lockedResult;
  }
  return result;
}

可以看到解码完成后,主要进行使用Transformation,并且转换的结果进行缓存。(感觉此处的处理会有重复,比如数据本身就是加载了已转换过的数据,此处又要进行编码和缓存到文件)

解码完成2

上述工作完成后会调用notifyEncodeAndRelease

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
  if (resource instanceof Initializable) {
    ((Initializable) resource).initialize();
  }

  Resource<R> result = resource;
  LockedResource<R> lockedResource = null;
  //因为解码工作是同步的,所以onResourceDecoded中会设置deferredEncodeManager
  if (deferredEncodeManager.hasResourceToEncode()) {
    lockedResource = LockedResource.obtain(resource);
    result = lockedResource;
  }
  //将结果分发出去
  notifyComplete(result, dataSource);

  stage = Stage.ENCODE;
  try {
    if (deferredEncodeManager.hasResourceToEncode()) {
      //将已转码的数据再编码并且缓存到文件中,主要工作在DiskLruCacheWrapper中
      deferredEncodeManager.encode(diskCacheProvider, options);
    }
  } finally {
    if (lockedResource != null) {
      lockedResource.unlock();
    }
  }
  //事情都做完了,进行资源回收,并将变量置空,提升gc效率
  onEncodeComplete();
}