RxJava 之基础知识

本文详细介绍了RxJava的基本概念,包括Observable、Observer及其订阅过程,HotObservable与ColdObservable的区别及转换方法,Flowable的特点及应用场景,Single、Completable和Maybe的特性,以及Subject和Processor在背压控制中的作用。

1. Observable

RxJava 的使用通常需要三步

  1. 创建 Observable Observable 的字面意思是被观察者,使用 RxJava 时需要创建一个被观察者,它会决定什么时候触发事件以及触发怎样的事件。有点类似上游发送命令,可以在这里决定异步操作模块的顺序和异步操作模块的次数。
  2. 创建 Observer Observer 即观察者,它可以在不同的线程中执行任务。这种模式可以极大地简化并发操作,因为它创建了一个处于待命状态的观察者哨兵,可以在未来某个时刻相应 Observable 的通知,而不需要阻塞等待 Observable 发送数据。
  3. 使用 subscribe() 进行订阅 创建了 Observable 和 Observer 之后,还需要使用 subscribe() 方法将它们连接起来,这样整个上下游就能链接起来实现链式调用。

subscribe 有多个重载的方法。

subscribe(onNext)
subscribe(onNext, onError)
subscribe(onNext, onError, onComplete)
subscribe(onNext, onError, onComplete, onSubscribe)
复制代码

在 subscribe 中 onComplete 是执行完了 onNext 之后再执行的。onComplete 是一个 Action,它与 Consumer 的区别如下:

  • Action: 无参数类型
  • Consumer:单一参数类型

在 RxJava 中,被观察者、观察者、subscribe() 方法三者缺一不可。只有使用了 subscribe(),被观察者才会开始发送数据。

RxJava2 的 5 中观察者模式如下:

类型描述
Observable能够发射 0 或 n 个数据,并以成功或错误事件终止
Flowable能够发射 0 或 n 个数据,并以成功或错误事件终止。支持被压,可以控制数据源发射的速度
Single只能发射单个数据或错误事件
Completable从来不发射数据,只处理 onComplete 和 onError 事件。可以看成 Rx 的 Runnable
Maybe能够发射 0 或者 1 个数据,要么成功,要么失败。

5 种观察者类型中只有 Flowable 支持背压,如果有需要背压的情况,则需使用 Flowable。

do 操作符

do 操作符可以给 Observable 的生命周期的各个阶段加上一系列的回调监听,当 Observable 执行到这个阶段是,这些回调就会被触发。在 RxJava 中包含了很多的 doXXX 操作符。

操作符用途
doOnSubscribe一旦观察者订阅了 Observable,他就会被调用
doOnLifeCycle可以在观察者订阅之后,设置是否取消订阅
doOnNext它产生的 Observable 每发射一项数据就会调用他一次,它的 Comsumer 接受发射的数据项。一般用于在 subscribe 之前对数据进行处理
doOnEach它产生的 Observable 每发射一项数据就会调用它一次,不仅包括 onNext,还包括 onError 和 onComplete
doAfterNext在 onNext 之后执行,而 doOnNext() 是在 onNext 之前执行
doOnComplete当它产生的 Observable 在正常终止调用 onComplete 时会被调用
doFinally在当它产生的 Observable 终止之后会被调用,无论是正常终止还是异常终止。
doAfterTerminate注册一个 Action,当 Observable 调用 onComplete 或 onError 时触发

2. Hot Observable 和 Cold Observable

1. Observable 的分类

在 RxJava 中,Observable 有 Hot 和 Cold 之分 Hot Observable 无论有没有观察者进行订阅,事件始终都会发生。当 Hot Observable 有多个订阅者时,Hot Observable 与订阅者们的关系是一对多的关系,可以与多个订阅者共享信息。 Cold Observable 是只有观察者订阅了,才开始执行发射数据流的代码。并且 Cold Observable 和 Observable 只能是一对一的关系。当有多个不同的订阅者时,消息是重新完整发送的。也就是说,对 Cold Observable 而言,有多个 Observer 的时候,它们各自的事件是独立的。

2. Cold Observable

Observable 的 just、create、range、fromXXX 等操作符都能生成 Cold Observable。

3. Cold Observable 如何转换成 Hot Observable

(1) 用 publish,生成 ConnectableObservable 使用 publish 操作符,可以让 Cold Observable 转换成 Hot Observable,它将原先的 Observable 转换成 ConnectableObservable。生成的 ConnectableObservable 需要调用 connect() 才能真正执行。ConnectableObservable 是线程安全的。

ConnectableObservable<Long> observable = Observable.create(new ObservableOnSubscribe<Long>() {
    @Override
    public void subscribe(ObservableEmitter<Long> observableEmitter) throws Exception {
        // do something...
    }
}).publish();
observable.connect();
observable.subscribe(subscriber1);
复制代码

(2) 使用 Subject/Processor Subject 和 Processor 的作用是相同的。Processor 是 RxJava 2.x 新增的类,继承自 Flowable,支持背压,而 Subject 不支持。

Observable<Long> observable = Observable.create(new ObservableOnSubscribe<Long>() {
    @Override
    public void subscribe(ObservableEmitter<Long> observableEmitter) throws Exception {
        // do something...
    }
}).observeOn(Schedulers.newThread());
PublishSubject<Long> subject = PublishSubject.create();
observable.subscribe(subject);

subject.subscribe(subscriber1);
复制代码

Subject 既是 Observable,又是 Observer(Subscriber)。Subject 继承自 Observable, 实现 Observer。 Subject 作为观察者,可以订阅目标 Cold Observable,使对方开始发送事件。同时它又作为 Observable 转发或者发送新的事件,让 Cold Observable 借助 Subject 转换为 Hot Obsevable。 Subject 并不是线程安全的,如果想要其线程安全,需调用toSerialized()方法(在 RxJava 1.x 中还可以用 SerializedSubject 代替 Subject,但在 RxJava 2.x 之后,SerializedSubject 不再是一个 public class)。

4. Hot Observable 转换成 Cold Observable

(1) ConnectableObservable 的 refCount 操作符 RefCount 操作符把从一个可连接的 Observable 连接和断开的过程自动化了。它操作一个可连接的 Observable,返回一个普通的 Observable。当第一个订阅者/观察者订阅这个 Observable 时,refCount 连接到下层的可连接 Observable。RefCount 跟踪有多少个观察者订阅它,直到最后一个观察者完成,才断开与下层可连接 Observable 的连接。 如果所有的订阅者/观察者都取消订阅了,则数据流停止;如果重新订阅,则重新开始数据流。 如果不是所有的订阅者/观察者都取消了订阅,而只是部分取消,则部分的订阅者/观察者重新开始订阅时,不会从头开始数据流。 (2) Observable 的 share 操作符 share 操作符封装了 publish().refCount() 调用。


3. Flowable

在 RxJava 2.x 中,Observable 不再支持背压,而改由 Flowable 来支持非阻塞式的背压。Flowable 是 RxJava 2.x 新增的被观察者,Flowable 可以看成 Observable 新的实现,它支持背压,同时实现 Reactive Stream 的 Publisher 接口。Flowable 所有的操作符强制支持背压,不过幸运的是,Flowable 中的操作符大多与 Observable 类似。 虽然 Flowable 与 Observable 很相似,但两者在使用场景上还是有区别的 使用 Observable 较好的场景如下:

  • 一般处理最大不超过 1000 条数据,并且几乎不会出现内存溢出;
  • GUI 鼠标事件,基本不会背压;
  • 处理同步刘 使用 Flowable 较好的场景如下:
  • 处理以某种方式产生超过 10KB 的元素;
  • 文件读取与分析;
  • 读取数据库记录,也是一个阻塞的和基于拉取模式;
  • 网络 I/O 流;
  • 创建一个响应式非阻塞接口。

4. Single、Completable 和 Maybe

通常情况下,如果想要使用 RxJava,首先会想到的是使用 Observable,如果考虑到背压的情况,则在 RxJava 2.x 下会使用 Flowable。除了 Observable 和 Flowable 外,在 RxJava 2.x 中还有 3 中类型的被观察者:Single、Completable 和 Mayb。

1. Single

从 SingleEmitter 的源码可以看出,Single 只有 onSuccess 和 onError 事件。其中,onSuccess() 用于发射数据(在 Observable/Flowable 中使用 onNext 来发射数据),而且只能发射一个数据,后面及时再发射数据也不会做任何处理。 Single 的 SingleObserver 中只有 onSuccess 和 onError,并没有 onComplete。这也是 Single 与其他 4 中被观察者之间的最大区别。Single 可以通过 toXXX 方法转换成 Observable、Flowable、Completable 及 Maybe。

2. Completable

Completable 在创建后,不会发射任何数据。Completable 只有 onComplete 和 onError 事件,同时 Completable 并没有 map、flatMap 等操作符,它的操作符比起 Observable/Flowable 要少得多。可以通过 fromXXX 操作符来创建一个 Completable。Completable 也可以通过 toXXX 方法转换成 Observable、Flowable、Single 及 Maybe。

3. Maybe

Maybe 是 RxJava 2.x 之后才有的新类型,可以看成 Single 和 Completable 的结合。Maybe 创建之后,MaybeEmitter 和 SingleEmitter 一样,并没有 onNext 方法,同样需要通过 onSuccess 方法来发射数据。Maybe 也只能发射 0 或 1 个数据,即使发射多个数据,后面发射的数据也不会处理。 如果 MaybeEmitter 先调用了 onComplete(),即使后面在调用 onSuccess(),也不会发射任何数据,同样也可以通过 toXXX 方法转换成其他被观察者。

5. Subject 和 Processor

1. Subject 的分类

Subject 包含 4 种类型,分别是 AsyncSubject、BehaviorSubject、ReplaySubject 和 PublishSubject。 (1) AsyncSubject Observer 会接收 AsyncSubject 的 onComplete() 之前的最后一个数据。

AsyncSubject<String> subject = AsyncSubject.create();
subject.onNext("asyncSubject1");
subject.onNext("asyncSubject2");
复制代码

subject.onComplete() 必须要调用才会开始发送数据,否则观察者将不接收任何数据。

(2) BehaviorSubject Observer 会先接收到 BehaviorSubject 被订阅前的最后一个数据,再接收订阅之后发射过来的数据。如果 BehaviorSubject 被订阅之前没有发送任何数据,则会发送一个默认数据。

BehaviorSubject<String> subject = BehaviorSubject.createDefault("behaviorSubject1");
复制代码

这里 behaviorSubject1 是默认值

(3) ReplaySubject ReplaySubject 会发射所有来自原始 Observable 的数据给观察者,无论它们是何时订阅的。

ReplaySubject<String> subject = ReplaySubject.create();
subject.onNext("replaySubject1");
subject.onNext("replaySubject2");
复制代码

稍微改一下代码,将 create() 改成 createWithSize(1),表示只缓存订阅前最后发送的一条数据。

ReplaySubject<String> subject = ReplaySubject.createWithSize(1);
复制代码

这个执行结果与 BehaviorSubject 的相同。但从并发的角度来看,ReplaySubject 在处理并发 subscribe() 和 onNext() 时会更加复杂。 ReplaySubject 除了可以限制缓存数据的数量,还能限制缓存的时间,使用 createWithTime() 即可。

(4) PublishSubject Observer 只接收 PublishSubject 被订阅之后发送的数据。

PublishSubject<String> subject = PublishSubject.create();
复制代码

(5) 使用 BehaviorSubject 实现预加载 预加载可以很好的提高程序的用户体验。每当用户处于弱网络时,打开一个 APP 很可能会出现一片空白或者一直在 loading,而如果能够预先加载一些数据,例如上一次打开 APP 时保存的数据,将会有效提升 APP 的用户体验。

public class RxPreLoader<T> {
    
    // 能够缓存订阅之前的最新数据
    private BehaviorSubject<T> mData;
    private Disposable disposable;

    public RxPreLoader(T defaultValue) {
        mData = BehaviorSubject.createDefault(defaultValue);
    }

    /**
     * 发送事件
     *
     * @param object
     */
    public void publish(T object) {
        mData.onNext(object);
    }

    /**
     * 订阅事件
     *
     * @param onNext
     * @return
     */
    public Disposable subscribe(Consumer<T> onNext) {
        disposable = mData.subscribe(onNext);
        return disposable;
    }

    /**
     * 取消订阅
     */
    public void dispose() {
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
            disposable = null;
        }
    }

    /**
     * 获取缓存数据的 Subject
     *
     * @return
     */
    public BehaviorSubject<T> getCacheDataSubject() {
        return mData;
    }

    /**
     * 直接获取最近的一个数据
     *
     * @return
     */
    public T getLastCacheData() {
        return mData.getValue();
    }
}
复制代码

2. Processor

Processor 和 Subject 的作用相同。Processor 是 RxJava 2.0 新增的功能,它是一个接口,继承自 Subscriber、Publisher,能够支持背压控制。 其实,Publisher 和 Processor 都是 Reactive Stream 的接口,Reactive Stream 项目提供了一个非阻塞异步流处理的背压标准。RxJava 2.0 已经基于 Reactive Stream 库进行重写,包括 Single、Completable 都是基于 Reactive Streams 规范重写的,Flowable 实现了 Reactive Streams 的 Publisher 接口等。 Reactive Streams 的目标是管制流数据在跨越异步边界进行流数据交换,可以认为是将元素传递到另一个线程或线程池,同时确保在接收端不是被迫缓冲任意数量的数据。换句话说,背压是该模型的重要组成部分,通过设置协调线程之间的队列大小进行限制。当各异步模型之间采用同步通信时会削弱异步带来的好处,因此必须采取谨慎措施,强制完全无阻塞反应流能在系统的各个方面做到异步实施。

Reactive Streams 规范的主要目目标:

  • 通过异步边界(Asynchronous Boundary)来解耦系统组件。解耦的先决条件,分离事件/数据流的发送方和接收方的资源使用。
  • 为背压处理定义一种模型。流处理的理想方式是将数据从发布者推送到订阅者,这样发布者就可以快速发布数据,同时通过压力处理来确保速度更快的发布者不会对速度较慢的订阅者造成过载。背压处理通过使用流控制来确保操作的稳定性并能实现优雅降级,从而提供弹性能力。

Reactive Streams JVM 接口由以下四个接口组成。

  • Publisher:消息发布者
  • Subscriber:消息订阅者
  • Subscription:一个订阅
  • Processor:Publisher + Subscriber 的结合体

RxJava 2.0 中使用 Processor 来处理背压。同时,在新发布的 Java 9 中也已经引入 Reactive Streams 的思想,具体来说是构建在 java.util.concurrent.Flow 容器中,包含了四个接口类。

转载于:https://juejin.im/post/5b8a54bb6fb9a019f928df8f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值