Fresco源码分析(4) - 异步加载数据

在这一章中,我将分析数据源DataSource的生成过程及它的作用。

1 时序图

根据Uri生成ProducerDataSource的过程概览:
DataSource

以上的时序图是精简过的,意在表达各个组件之间的调用顺序和功能。

2 通过Url获取DataSourceSupplier

第三章末尾我们讲到DraweeControllerBuilder会根据Uri初始化DataSourceSupplier,那么现在来看看是怎么初始化的。

首先会根据各种情况判断生成不同的DataSource:

protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier() {

    //...

    if (mImageRequest != null) {
      supplier = getDataSourceSupplierForRequest(mImageRequest);
    } else if (mMultiImageRequests != null) {
      supplier = getFirstAvailableDataSourceSupplier(mMultiImageRequests);
    }

我们看到Fresco会根据是否有多个图片请求,如果有,则会取多个图片请求中最先能请求到的作为DataSource。

if (supplier != null && mLowResImageRequest != null) {
  List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList<>(2);
  suppliers.add(supplier);
  suppliers.add(getDataSourceSupplierForRequest(mLowResImageRequest));
  supplier = IncreasingQualityDataSourceSupplier.create(suppliers);
}

在之后会判断是否有提供低分辨率的图片,如果有,则创建一个清晰度逐渐提高的DataSource(默认只有两级清晰度)。更多关于多图请求的内容参照多图请求及图片复用

在此我们就分析根据一个ImageRequest获取DataSource的过程,获取其他种类DataSource都是通过它实现的。

protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
  ImageRequest imageRequest,
  Object callerContext,
  boolean bitmapCacheOnly) {
    if (bitmapCacheOnly) {
      return mImagePipeline.fetchImageFromBitmapCache(imageRequest, callerContext);
    } else {
      return mImagePipeline.fetchDecodedImage(imageRequest, callerContext);
    }
}

这个函数给我们传递了两个信息:

  • Fresco中数据的传递基本都是包装在CloseableReference中的
  • 它会根据bitmapCacheOnly(是否只从已解码的内存缓存中获取数据)来获取不同的DataSource。

那我们就来看看它不限制缓存级别的时候是怎么处理的(这种情况下会包括bitmapCacheOnly时候的内容)。

public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
  ImageRequest imageRequest,
  Object callerContext) {
    try {
      Producer<CloseableReference<CloseableImage>> producerSequence =
          mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
      return submitFetchRequest(
          producerSequence,
          imageRequest,
          ImageRequest.RequestLevel.FULL_FETCH,
          callerContext);
    } catch (Exception exception) {
      return DataSources.immediateFailedDataSource(exception);
    }
}

我们发现它会首先根据传入的ImageRequest产生一个Producer,它为不同ImageRequest提供了不同的数据传送的管道。(关于Producer具体见Fresco源码分析(5) - Producer

submitFetchRequest函数中做了三件事:

  1. ImageRequestLowestPermittedRequestLevel和传入的RequestLevel中最高的一级作为此次数据获取的最高缓存获取层;
  2. ImageRequest、本次请求的唯一标识、ImageRequestListener(提供ImageRqeuest事件的回调)、是否需要渐进式加载图片等信息封装进SettableProducerContext
  3. 创建AbstractproducerToDataSourceAdapter,它实际上是一种DataSource,在这个过程中会让producer通过SettableProducerContext获取数据。

至此我们就获取了所需要的DataSource,并将它设置给DraweeController。

3 各类DataSource

上一节中我们获取了一个AbstractproducerToDataSourceAdapter作为DataSource,你可能对它的功能不太理解,这一节将介绍各类DataSource的功能。

3.1 基类

DataSource的原型

public interface DataSource<T> {

    public boolean isClosed();

    //是否得到结果
    boolean hasResult();

    //获取现在得到的结果
    @Nullable T getResult();

    //是否结束
    boolean isFinished();

    //是否失败
    boolean hasFailed();

    //获取失败原因
    @Nullable Throwable getFailureCause();

    //获取加载进度
    float getProgress();

    //释放资源
    boolean close();

    //绑定DataSubScriber
    void subscribe(DataSubscriber<T> dataSubscriber, Executor executor);
}

Fresco接着实现了一个基础类AbstractDataSource,维持着IN_PROGRESS、SUCCESS、FAILURE三种状态。它除了实现closesubscribe的之外,添加了这几个重要函数及其实现:

  • setResultInternal(@Nullable T value, boolean isLast) 当DataSource关闭或状态非IN_PROGRESS时返回false,否则设置Result并返回ture,若isLast为true,则设置状态为SUCCESS;
  • setFailureInternal(Throwable throwable) 当DataSource关闭或状态非IN_PROGRESS时返回false,否则设置失败原因、状态为FAILURE并返回true;
  • setProgressInternal(float progress) 当DataSource关闭或状态非IN_PROGRESS时返回false,若目标进度小于已有进度也返回false,否则更新进度并返回true;
  • notifyDataSubscriber(DataSubscriber<T> subscriber, Executor executor, boolean isFailure, boolean isCancellation) 在指定的Executor上根据isFailure、isCancellation调用DataSubscriber的对应函数(若都为false即成功加载),由此更新数据;
  • notifyDataSubscribers() 通知所有绑定的DataSubscriber更新数据;
  • setResult/setFailure/setProgress 调用对应setXXXInternal,当其返回true时,调用notifyDataSubscribers()

3.2 AbstractProducerToDataSourceAdapter

AbstractProducerToDataSourceAdapter也是一种DataSource,它继承AbstractDataSource,包装了Producer取数据的过程,非常重要,所以单独列出来。

它在创造过程中主要做了两件事:

  1. 创建一个Consumer,在newResult、Failure、Cancel、ProgressUpdate几个状态函数中调用自己的相应实现;
  2. 对之前根据ImageRequest创造出来的的Producer调用produceResults(Consumer consumer, ProducerContext context),让它开始加载数据。

我们用一个直观的图来看看当Producer产生新结果(newResult)时的调用顺序是什么样的:

NewResult

当失败(Failure)、取消(Cancel)、更新进度(ProgressUpdate)时的操作流程是类似的。我们可以看出,AbstractProducerToDataSourceAdapter是连接Producer与DataSource的纽带。

AbstractProducerToDataSourceAdapter的构造方法是protected,只能通过继承的类提供的办法来构造它。以下是两个继承了AbstractProducerToDataSourceAdapter的类:

  • CloseableProducerToDataSourceAdapter 提供create方法创建实例,实现了closeResult方法,会在AbstractDataSource销毁时同时销毁收到的Result,是主要使用的DataSouce
  • ProducerToDataSourceAdapter 提供create方法创建实例,没有实现别的方法。此DataSource仅仅用于预加载图片

3.3 其他DataSource

IncreasingQualityDataSource

它内部维持着一个AbstractDataSource(可以说是CloseableProducerToDataSourceAdapter)列表,DataSource提供数据的清晰度由后往前递增

它会为列表中的每一个DataSource绑定一个DataSubscriber(IncreasingQualityDataSourceSupplier.InternalDataSubscriber),它负责保证每次只能获取清晰度更高的DataSource数据,获取数据同时会销毁数据清晰度更低的DataSource。

FirstAvailableDataSource

它内部维持着一个AbstractDataSource(可以说是CloseableProducerToDataSourceAdapter)列表,它会返回这里面首先能获取到数据的DataSource。

同样,它也会为列表中的DataSource绑定DataSubscriber(FirstAvailableDataSourceSupplier.InternalDataSubscriber),如果数据加载成功,那么就设定指定DataSource为目标DataSource;如果加载数据失败,则跳转到列表下一个DataSource继续尝试加载。

SettableDataSource

它继承AbstractDataSource,并将重写settResultsetFailuresetProgress在内部调用父类的相应函数,但是修饰符变成了public(原来是protected)。即使用SettableDataSource时可以在外部调用这三个函数设置DataSource状态。一般用于在获取DataSource失败时直接产生一个设置为Failure的DataSource。

关于数据源的其他内容,可以参考数据源/订阅数据源