RxJava2 中 doFinally 和 doAfterTerminate 的比较

简介: RxJava2 中 doFinally 和 doAfterTerminate 的比较

在 RxJava 中 doFinally 和 doAfterTerminate 这两个操作符很类似,都会在 Observable 的 onComplete 或 onError 调用之后进行调用。


使用了这两个操作符在 Observable 结束后,会调用 doFinally、doAfterTerminate 所提供的 Action。


这两个操作符虽然有一定的相似度,但他们依然有差别。并且两者在使用时,会存在调用的先后顺序。


doAfterTerminate



从 doAfterTerminate 操作符的源码来看

@CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Observable<T> doAfterTerminate(Action onFinally) {
        ObjectHelper.requireNonNull(onFinally, "onFinally is null");
        return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, onFinally);
    }


它调用的是 doOnEach() 方法。


其实,doOnNext、doOnError、doOnComplete、doOnTerminate 等这些操作符也是调用 doOnEach() 方法。


doOnEach() 实际上调用的是 ObservableDoOnEach 类。( RxJavaPlugins.onAssembly 本身是一个 hook 方法,会返回一个 Observable 对象。)


doOnEach() 需要四个参数:onNext、onError、onComplete、onAfterTerminate。

doAfterTerminate 操作符的参数 onFinally 对应的是 onAfterTerminate。

@CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    private Observable<T> doOnEach(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Action onAfterTerminate) {
        ObjectHelper.requireNonNull(onNext, "onNext is null");
        ObjectHelper.requireNonNull(onError, "onError is null");
        ObjectHelper.requireNonNull(onComplete, "onComplete is null");
        ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null");
        return RxJavaPlugins.onAssembly(new ObservableDoOnEach<T>(this, onNext, onError, onComplete, onAfterTerminate));
    }


ObservableDoOnEach 继承了 Observable 类,并实现了它的抽象方法 subscribeActual()。该方法是 Observable 和 Observer 连接的纽带。其中,source 代表了被观察者 Observable 本身,而 DoOnEachObserver 是实际的观察者。

@Override
    public void subscribeActual(Observer<? super T> t) {
        source.subscribe(new DoOnEachObserver<T>(t, onNext, onError, onComplete, onAfterTerminate));
    }


从 DoOnEachObserver 中的 onError()、onComplete() 方法中可以看到 onAfterTerminate 是在 downstream.onError(t) 或者 downstream.onComplete() 之后,才执行 run()。这也符合我们最初对它的认识。

@Override
        public void onError(Throwable t) {
            if (done) {
                RxJavaPlugins.onError(t);
                return;
            }
            done = true;
            try {
                onError.accept(t);
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                t = new CompositeException(t, e);
            }
            downstream.onError(t);
            try {
                onAfterTerminate.run();
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                RxJavaPlugins.onError(e);
            }
        }
        @Override
        public void onComplete() {
            if (done) {
                return;
            }
            try {
                onComplete.run();
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                onError(e);
                return;
            }
            done = true;
            downstream.onComplete();
            try {
                onAfterTerminate.run();
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                RxJavaPlugins.onError(e);
            }
        }


doFinally



doFinally 是 RxJava 在 2.0.1 版本新增的操作符。


doFinally 除了拥有 doAfterTerminate 的特性之外,还会在下游(downstream)取消时被调用。这是 doFinally 和 doAfterTerminate 最大的区别。


同样,看一下 doFinally 的源码。它调用的是 ObservableDoFinally 类。

public final Observable<T> doFinally(Action onFinally) {
        ObjectHelper.requireNonNull(onFinally, "onFinally is null");
        return RxJavaPlugins.onAssembly(new ObservableDoFinally<T>(this, onFinally));
    }


在 ObservableDoFinally 类中,从 subscribeActual() 可以看出它的实际观察者是 DoFinallyObserver 类。其中,onFinally 是 doFinally 操作符所传递的参数。

@Override
    protected void subscribeActual(Observer<? super T> observer) {
        source.subscribe(new DoFinallyObserver<T>(observer, onFinally));
    }


在 DoFinallyObserver 类中的 onError、onComplete、dispose 方法中都会调用 runFinally() 方法。而 runFinally() 执行的正是 onFinally 的 run()。

@Override
        public void onError(Throwable t) {
            downstream.onError(t);
            runFinally();
        }
        @Override
        public void onComplete() {
            downstream.onComplete();
            runFinally();
        }
        @Override
        public void dispose() {
            upstream.dispose();
            runFinally();
        }
        ......
        void runFinally() {
            if (compareAndSet(0, 1)) {
                try {
                    onFinally.run();
                } catch (Throwable ex) {
                    Exceptions.throwIfFatal(ex);
                    RxJavaPlugins.onError(ex);
                }
            }
        }


所以,从源码可以得出在 doFinally 是在观察者执行完 onError、onComplete 或取消时执行的操作。


二者的顺序



doFinally 和 doAfterTerminate 都会在 onComplete 之后才执行,那么它们二者的顺序是如何呢?


不妨写一段代码测试一下:

Observable.just("Hello World")
                .doOnNext(s -> System.out.println("doOnNext:"))
                .doFinally(() -> System.out.println("doFinally:"))
                .doAfterTerminate(() -> System.out.println("doAfterTerminate:"))
                .subscribe(
                        s -> System.out.println("onNext:" + s),
                        throwable -> System.out.println("onError:"),
                        () -> System.out.println("onComplete:"));


执行结果:

doOnNext:
onNext:Hello World
onComplete:
doAfterTerminate:
doFinally:


发现 doFinally 是在 doAfterTerminate 之后调用。


那么交换一下它们的顺序会如何呢?

Observable.just("Hello World")
                .doOnNext(s -> System.out.println("doOnNext:"))
                .doAfterTerminate(() -> System.out.println("doAfterTerminate:"))
                .doFinally(() -> System.out.println("doFinally:"))
                .subscribe(
                        s -> System.out.println("onNext:" + s),
                        throwable -> System.out.println("onError:"),
                        () -> System.out.println("onComplete:"));


执行结果:

doOnNext:
onNext:Hello World
onComplete:
doFinally:
doAfterTerminate:


这一次,doFinally 先执行而 doAfterTerminate 后执行。


因为,它们都需要在 downstream.onComplete() 执行之后,才会执行。而 downstream 对应的下游是观察者。下流的数据流向跟上游的数据流向是相反的,从下向上的。所以,离观察者越近,就越先执行。这就是两段代码执行顺序不同的缘故。


最后,写一个极端一点的例子,先后调用 doFinally、doAfterTerminate、doFinally、doAfterTerminate:

Observable.just("Hello World")
                .doOnNext(s -> System.out.println("doOnNext:"))
                .doFinally(() -> System.out.println("doFinally1:"))
                .doAfterTerminate(() -> System.out.println("doAfterTerminate1:"))
                .doFinally(() -> System.out.println("doFinally2:"))
                .doAfterTerminate(() -> System.out.println("doAfterTerminate2:"))
                .subscribe(
                        s -> System.out.println("onNext:" + s),
                        throwable -> System.out.println("onError:"),
                        () -> System.out.println("onComplete:"));


执行结果:

doOnNext:
onNext:Hello World
onComplete:
doAfterTerminate2:
doFinally2:
doAfterTerminate1:
doFinally1:


在 onComplete 调用之后,先打印了"doAfterTerminate2:",再打印"doFinally2:",然后打印"doAfterTerminate1:",最后打印"doFinally1:"。这正好符合刚才的分析。


总结



本文是对 doFinally 和 doAfterTerminate 两个操作符的总结。


相关文章
|
关系型数据库 开发工具 C语言
PostgreSQL libpq开发入门
简单入门C语言开发基于PostgreSQL libpq应用
|
7月前
|
Android开发 开发者
Android利用SVG实现动画效果
本文介绍了如何在Android中利用SVG实现动画效果。首先通过定义`pathData`参数(如M、L、Z等)绘制一个简单的三角形SVG图形,然后借助`objectAnimator`实现动态的线条绘制动画。文章详细讲解了从配置`build.gradle`支持VectorDrawable,到创建动画文件、关联SVG与动画,最后在Activity中启动动画的完整流程。此外,还提供了SVG绘制原理及工具推荐,帮助开发者更好地理解和应用SVG动画技术。
347 30
|
7月前
|
存储 弹性计算 安全
阿里云服务器ECS实例选购参考:vCPU到云盘IOPS等指标详解
阿里云服务器ECS实例可以分为多种实例规格族,而根据CPU、内存等配置的不同,一种实例规格族又进一步细分为多种实例规格。这些实例规格包含了众多关键的性能指标,如 vCPU、处理器、内存、vTPM、本地存储、网络带宽、网络收发包 PPS、连接数、弹性网卡、云盘带宽、云盘 IOPS 等。深入理解这些性能指标,对于用户在阿里云服务器购买过程中选择最适合自己业务需求的实例规格至关重要。
|
12月前
|
数据采集 机器学习/深度学习 人工智能
《驯服“过拟合”之兽:守护人工智能算法的精准与稳健》
在人工智能发展中,过拟合是算法训练中常见问题,指模型过度学习训练数据中的细节和噪声,导致对新数据泛化能力差。为避免过拟合,需从数据质量和数量入手,确保数据多样性并适当增加数据量。同时,数据预处理(如归一化)、选择合适的模型复杂度、应用正则化技术(如L1/L2正则化)、采用早停法和交叉验证等方法,可有效提高模型的稳定性和准确性。防范过拟合至关重要,尤其在医疗、金融等领域,以确保算法的可靠性和实用性。
211 17
|
11月前
|
人工智能 运维 API
PAI企业级能力升级:应用系统构建、高效资源管理、AI治理
PAI平台针对企业用户在AI应用中的复杂需求,提供了全面的企业级能力。涵盖权限管理、资源分配、任务调度与资产管理等模块,确保高效利用AI资源。通过API和SDK支持定制化开发,满足不同企业的特殊需求。典型案例中,某顶尖高校基于PAI构建了融合AI与HPC的科研计算平台,实现了作业、运营及运维三大中心的高效管理,成功服务于校内外多个场景。
|
Java 数据库
成功解决: 加上 @Transient 仍然报 Unknown column ‘goods_list‘ in ‘field list‘
这篇文章讨论了在SpringBoot结合MyBatis-Plus框架中,当实体类中包含另一个实体类的集合,而这个集合字段在数据库中不存在时,如何避免由此引发的错误。文章提供了两种解决方法:一是使用`@TableField(exist = false)`注解明确指定该字段在数据库中不存在;二是使用`transient`关键字,但要注意`transient`关键字在Java中默认就是被忽略的,不需要加`@Transient`注解。文章最后展示了问题解决的效果。
|
机器学习/深度学习 数据采集 算法
Python实现支持向量机SVM回归模型(SVR算法)项目实战
Python实现支持向量机SVM回归模型(SVR算法)项目实战
|
芯片 内存技术
MDK 下载程序出现no target connected 导致下载失败
MDK 下载程序出现no target connected 导致下载失败
795 0
MDK 下载程序出现no target connected 导致下载失败
|
小程序 容器
【微信小程序】image组件的4种缩放模式与9种裁剪模式
假设有一个容器(这个容器的宽高就是设置的样式),要将图片放进去。而aspectFit的特点就是保持图片不变形,且容器要“刚好”将这个图片装进去。如果原始图片比容器大,就要被等比例缩小;如果原始图片比容器小,就会被等比例放大。一直放大或缩小到图片的某一条边刚好和容器的一条边重合。
2121 0
|
Java 编译器
Janino学习记录
Janino学习记录
1297 0