Fresco正传(4):DraweeController分析

前言

一口气写了好多了,先配张美女图,放松下眼睛。

言归正传,在总览的文章中就分析过,DraweeController是控制数据逻辑的核心,数据的请求也会在这部分发出。但是,一口气写下来,估计内容会太多,所以本篇博客只分析DraweeController的体系结构以及内部方法和介绍一些延伸性的东西。至于,如何发出请求获取图片的,后续博客再分析。

当然还是先给出DraweeController的继承体系,看看能推测出来一些什么。

DraweeController不用说,肯定是一些抽象共性的东西。而AbstractDraeeController应该是在抽象的基础上,封装了一些共性操作,这也非常符合现在开发技术:通过接口抽象,并给出抽象的共性操作实现,具体怎么做、做什么交给你。那么VolleyDraweeControllerPipelineDraweeController就应该是具体使用什么样的方法来做事了。

正文

自上而下的分析类以及类中典型的方法。

DraweeController

先分析下类注释,看看能发现什么:

  1. DraweeController会被DraweeView所使用。
  2. DraweeView会转发事件给DraweeController,而DraweeController会基于事件控制DraweeHierarchy顶层视图的切换。

简单几句话描述了DraweeController身为控制器的核心作用:逻辑处理。

往下看,onAttach()是不是很眼熟,当DraweeView包含的层级结构被附加在window上时被调用。

还记得在第二篇分析中,博文最后分析DrwaeeHolderattachController()方法吗?没错,哪里调用的就是这个onAttach()方法。而且,我们说过在调用attachController()之所以会有这么多限制条件,肯定是由于这里有重要的逻辑发生。在onAttach()方法中获取数据源、拿到图片、并最后通过观察者设置实际显示的图片,具体的细节稍后再说。

AbstractDraweeController

在正式分析AbstractDraweeController之前还有个小插曲,那就是DataSourceDataSubscriber的关系。

如果你有关心RxJava那么你一定看过这篇文章:给 Android 开发者的 RxJava 详解RxJava是响应式编程的一部分,而响应式编程则充分利用了和扩展了观察者模式,使RxJava非常强大。

如果你有观察者的基础,那么你一定一眼就明白DataSource是被观察者,而DataSubscriber是观察者。在被观察者存储着观察者的数据集合,以用来及时通知事件。

public interface DataSource<T> {

  /**
   * 通知订阅者数据源状态的改变
   * Subscribe for notifications whenever the state of the DataSource changes.
   */
  void subscribe(DataSubscriber<T> dataSubscriber, Executor executor);
}

在来看一下DataSubscriber都有什么。值得一提的是,DataSource使用完后,必须要求被安全的关闭。

public interface DataSubscriber<T> {
  void onNewResult(DataSource<T> dataSource);
  void onFailure(DataSource<T> dataSource);
  void onCancellation(DataSource<T> dataSource);
  void onProgressUpdate(DataSource<T> dataSource);
}

所以,就有了BaseDataSubscriber类,实现了DataSubscriber接口,并在finally中安全关闭了数据源。 这个类AbstractDraweeController中会用到。

public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> {

    @Override
    public void onNewResult(DataSource<T> dataSource) {
        try {
            onNewResultImpl(dataSource);
        } finally {
            if (dataSource.isFinished()) {
                dataSource.close();
            }
        }
    }

    // ... 

    protected abstract void onNewResultImpl(DataSource<T> dataSource);

    protected abstract void onFailureImpl(DataSource<T> dataSource);
}

正式开始AbstractDraweeController的分析。先看看类注释能告诉我们什么:

  1. 实现了一些通用的功能,并不涉及具体是怎样抓取图片的。
  2. 所有的方法都应该在UI线程调用。

再看看构造方法,在其中调用了init()方法,主要是做了一些状态的初始化:

  1. 取消延时释放
  2. 重置启动状态
  3. 重新初始化重试、手势、监听器
  4. 清除旧的层次结构
  5. 重置状态常量

虽然代码很多,但是都可以忽略不计的逻辑,实际上我们也不关心这些。接下来就看看非常重要的onAttach()方法。

 @Override
public void onAttach() {
    Preconditions.checkNotNull(mSettableDraweeHierarchy);
    mDeferredReleaser.cancelDeferredRelease(this);
    mIsAttached = true;
    if (!mIsRequestSubmitted) {
        submitRequest();
    }
}

检查SettableDraweeHierarchy可设置的层次机构不许为NULL,否则直接报异常。是否是请求状态,是就调用submitRequest()方法发送请求。

从这里开始会完成一些列复杂的操作,先看一张图吧。

这里写图片描述

protected void submitRequest() {
        ...
        mSettableDraweeHierarchy.setProgress(0, true);
        ...
        mDataSource = getDataSource();
        ...
        // 此处以第一次请求为例,所以wasImmediate为false
        final boolean wasImmediate = mDataSource.hasResult();

        //------------创建dataSubscriber的匿名内部类,交由AbstractDraweeController处理
        final DataSubscriber<T> dataSubscriber =
                new BaseDataSubscriber<T>() {
                    @Override
                    public void onNewResultImpl(DataSource<T> dataSource) {
                        // isFinished must be obtained before image, otherwise we might set intermediate result
                        // as final image.
                        boolean isFinished = dataSource.isFinished();
                        float progress = dataSource.getProgress();
                        T image = dataSource.getResult();
                        if (image != null) {
                            onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
                        } else if (isFinished) {
                            onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
                        }
                    }

                    @Override
                    public void onFailureImpl(DataSource<T> dataSource) {
                        onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
                    }

                    @Override
                    public void onProgressUpdate(DataSource<T> dataSource) {
                        boolean isFinished = dataSource.isFinished();
                        float progress = dataSource.getProgress();
                        onProgressUpdateInternal(id, dataSource, progress, isFinished);
                    }
                };

        // 给数据源注册观察者
        mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
    }

整个方法做了如下的事情:

  1. 设置加载进度为0
  2. 获取数据源,这句话是核心语句。并且getDataSource()是个抽象方法,交给子类具体处理。
  3. 创建数据源观察者,并在内部类的onNewResultImpl()方法,调用AbstarctDraweeController的方法onNewResultInternal()处理数据源获取的结果。
  4. 为数据源注册观察者。等待回调的产生。

BaseDataSubscriberonNewResultImple()方法的逻辑就简单了,看看数据是否加载完毕和加载的进度情况,如果数据源结果不为空就调用onNewResultInternal()方法进一步处理。

看看onNewResultInternal()方法做了什么事:

  1. 检查是否是自己想要的结果,如果不是,直接释放资源。
  2. 如果是想要的数据源,创建对应的drawable。
  3. 调用hierarchy.setImage(draweavke)设置当前显示的draweable。
  4. 调用getControllerListener().onFilalImageSet()回调图像设置成功的结果。
  5. 最后在finally中释放之前缓存的drawable对象和Image对象。
private void onNewResultInternal(
      String id,
        DataSource<T> dataSource,
        @Nullable T image,
        float progress,
        boolean isFinished,
        boolean wasImmediate) {

    // 检查是否是自己想要的结果,如果不是,直接释放资源
    ....

    // 如果是想要的数据源,创建对应的drawable,设置当前显示的drawable,释放之前缓存的drawable对象和Image对象
    Drawable drawable = createDrawable(image);
    ... 
    try {
        // 设置当前显示的drawable
        if (isFinished) {
            mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
            // 回调设置图像结束
            getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
        } 
        ... 
    } finally {
        // 释放之前缓存的drawable对象和Image对象
        ....
    }
}

至此,AbstractDraweeController类的分析就基本结束。

PipelineDraweeController

为什么选择分析PipelineDraweeController?,还记得在Fresco正传(2):DraweeView分析,分析到Fresco类源码时调用的initializeDrawee()方法么?

private static void initializeDrawee(Context context) {
 sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
 SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
}

这里表明默认使用的Controller是PipelineDraweeController

看一下类注释,看看能够了解到什么信息:

  1. DraweeController是ImagePipeline和SettableDraweeHierarchy的桥梁。
  2. 实际显示的图像是数据源提供的,而数据源是在attachdetach时自动获取和关闭的。

还记得在分析AbstractDraweeController时提到过的getDataSource()方法吗?这里有实现,看看是怎么样的:

Override
protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
    return mDataSourceSupplier.get();
}

先上一张图,清醒清醒,看看getDataSource()是怎么样被调用的。

哈哈。我怎么觉得画的特别棒呢。

想要数据源,则需要数据源提供者提供mDataSourceSupplier,在init()方法中,成员变量mDataSourceSupplier被赋值。

private void init(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier) {
    mDataSourceSupplier = dataSourceSupplier;
}

init()方法在PipelineDraweeController的构造方法中调用,在外部传入了dataSourceSupplier参数。通过alt+f7 看看哪里调用构造方法,发现在PipilineDraweeControllerFactory工厂中的newController()方法,构造了PipelineDraweeController类的实例,并将newController()dataSourceSupplier参数传入了构造方法中。

public PipelineDraweeController newController(
        Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier,
        String id,
        Object callerContext) {

    // 创建控制器
    return new PipelineDraweeController(
            mResources,
            mDeferredReleaser,
            mAnimatedDrawableFactory,
            mUiThreadExecutor,
            dataSourceSupplier,
            id,
            callerContext);
}

再跟踪一下哪里调用了工厂的newController()方法。发现是PipelineDraweeControllerBuilerobtainController()调用了。更深入的细节,例如:obtainDataSourceSupplier()先不分析,先把思路捋清楚。

@Override
protected PipelineDraweeController obtainController() {
    DraweeController oldController = getOldController();
    PipelineDraweeController controller;

    // 如果已经有旧的contoller
    if (oldController instanceof PipelineDraweeController) {
        controller = (PipelineDraweeController) oldController;
        controller.initialize(
                obtainDataSourceSupplier(),
                generateUniqueControllerId(),
                getCallerContext());
    } else {
        // 第一次请求
        controller = mPipelineDraweeControllerFactory.newController(
                obtainDataSourceSupplier(),
                generateUniqueControllerId(),
                getCallerContext());
    }

    // 返回build 所需的controller
    return controller;
}

在跟踪一下哪里调用了obtainController()方法,发现是在buildController()方法中调用的。而又在AbstractDraweeControllerBuilderbuild()方法中,调用了buildController()方法。

protected AbstractDraweeController buildController() {
    AbstractDraweeController controller = obtainController(); // 核心,获取controller

    //
    controller.setRetainImageOnFailure(getRetainImageOnFailure());
    maybeBuildAndSetRetryManager(controller);
    maybeAttachListeners(controller);
    return controller;
}
@Override
public AbstractDraweeController build() {
    validate();

    // if only a low-res request is specified, treat it as a final request.
    if (mImageRequest == null && mMultiImageRequests == null && mLowResImageRequest != null) {
        mImageRequest = mLowResImageRequest;
        mLowResImageRequest = null;
    }

    return buildController();
}

最后,是哪里调用的AbstractDraweeControllerBuilderbuild()方法? 这次你一定很熟悉了,是在SimpleDraweeView类的setImageURI()方法中。构建出DraweeController后,将其交给了DraweeView的体系。

public void setImageURI(Uri uri, @Nullable Object callerContext) {

    DraweeController controller = mSimpleDraweeControllerBuilder
            .setCallerContext(callerContext)
            .setUri(uri)
            .setOldController(getController())
            .build();


    setController(controller);
}

最后的问题,mSimpleDraweeControllerBuiler是哪里来的?相信不用我多说了吧。

这样整个获取数据源的流程,我们就串联起来了。在看一下这个图,是不是更清晰了?

最后

DraweeController的体系分析就基本完成了,如果觉得对您有帮助,多多留言哦。

下篇分析是如何发出请求的。

github:https://github.com/biezhihua

Fresco正传(3):DraweeHierarchy分析:http://blog.csdn.net/biezhihua/article/details/49800313

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值