【从零学 RxJava】RxJava 1 Hot Observable & Cold Observable、Subject
前言
什么是 RxJava
RxJava 是 Rx 在 Java 上的实现,用于通过使用 Observable/Flowable 序列来构建异步和基于事件的程序的库
什么是 Rx
Rx,ReactiveX,Reactive Extensions,是一种编程模式,目标是提供一致的编程接口,帮助开发者更方便地处理异步数据流。Rx 库几乎支持所有地编程语言没比如 .NET
JavaScript
C++
Java
本章节介绍 Hot Observable & Cold Observable 相关概念和 API 示例
版本
3.0.4
基础概念
Observable
被观察者,使用 RxJava 时需要创建一个被观察者,用于发送事件,类比于 设计模式:观察者模式 的被观察者
Hot Observable
Hot Observable 无论有没有 观察者 进行订阅,事件始终都会发生,一个 Hot Obsevable 可以对应多个 订阅者
Hot Observable 可以类比于 广播电台
Cold Observable
Cold Observable 是只有 观察者 订阅了,才开始发射数据,Cold Observable 和 Observer 是一一对应的
Cold Observable 可类比于 仅供个人欣赏的CD
Observer
观察者,可以在不同的线程中执行任务,而不需要阻塞,响应 Observable 的通知,同样类比于 设计模式:观察者模式 的观察者
subscribe
动作,订阅,连接 Observable 和 Observer
Subject
Subject 既是 Observable,又是 Oberver,官网称可以将 Subject 看作一个桥梁或者代理
API 示例
GAV
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>3.0.4</version>
</dependency>
hello world
第一步,从 hello world
直观的认识 RxJava 的编程模式
public static void main(String[] args) {
Observable.just("hello world")
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
System.out.println(s);
}
});
// Lambda
// Observable.just("hello world").subscribe(t -> System.out.println(t));
/**
* subscribe 的重载
* Observable#subscribe(Consumer onNext, Consumer onError, Action onComplete)
*/
Observable.just("hello world 2")
.subscribe(
// onNext
new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
System.out.println(s);
}
}
// onError
, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Throwable {
System.out.println("error");
}
}
// onComplete
, new Action() {
@Override
public void run() throws Throwable {
System.out.println("onComplete");
}
}
);
// Lambda
/*Observable.just("test2")
.subscribe(
t -> System.out.println(t)
, e -> System.out.println("error")
, () -> System.out.println("complete")
);*/
}
此处,Observable#just
方法创建一个发射简单字符串的 Observable,匿名 Consumer 订阅该信息并输出 hello world
借助 Lambda 表达式会更加简洁优雅
同样,其 重载方法 支持多种回调,如 onNext
onError
onComplete
do 操作符
可以控制 Observable 的生命周期
public static void main(String[] args) {
Observable.just("test")
// 每发射一条数据就会被调用一次,在 subscribe 发生
.doOnNext(t -> System.out.println("doOnNtext " + t))
// 在 Consumer#onNext 之后调用
.doAfterNext(t -> System.out.println("doAfterNext " + t))
// 在 Observable 正常终止时调用
.doOnComplete(() -> System.out.println("doOnComplete"))
// 一旦被订阅,就会被调用,返回 Disposable
.doOnSubscribe(d -> System.out.println("doOnSubscribe " + d.isDisposed()))
// 当 Observable 调用 onComplete 或 onError 会调用,在 doFinally 之后
.doAfterTerminate(() -> System.out.println("doAfterTerminate"))
// 当 Observable 调用 onComplete 或 onError 会调用
.doFinally(() -> System.out.println("doFinally"))
// 当 Observable 每发射一项数据,都会调用一次,返回 Notification
.doOnEach(n -> System.out.println("doOnEach "
+ (n.isOnNext() ? "onNext" : n.isOnComplete() ? "onComplete" : "onError")))
// 在订阅之后,返回 Disposable,可以设置是否取消订阅
.doOnLifecycle(
d -> System.out.println("doOnLifecycle " + d.isDisposed())
, () -> System.out.println("doOnLifecycle action"))
.subscribe(t -> System.out.println(t));
}
// 结果
doOnSubscribe false
doOnLifecycle false
doOnNtext test
doOnEach onNext
test
doAfterNext test
doOnComplete
doOnEach onComplete
doFinally
doAfterTerminate
具体 生命周期 的触发时机见代码注释
Hot Observable & Cold Observable
Observable 的 just
create
等操作符生成的都是 Cold Observable,比如示例中的 Observable#just
,同样它可以转换成 Hot Observable
Cold Observable
static void coldObservable() throws InterruptedException {
Observable o = Observable.create(e -> {
Observable.interval(
10
, TimeUnit.MILLISECONDS
, Schedulers.computation()
)
.take(Integer.MAX_VALUE)
.subscribe(e::onNext);
})
.observeOn(Schedulers.newThread());
o.subscribe(t -> System.out.println("c1: " + t));
o.subscribe(t -> System.out.println(" c2: " + t));
Thread.sleep(200);
}
// 结果:
c1: 0
c2: 0
c2: 1
c1: 1
c2: 2
c1: 2
c2: 3
c2: 4
c1: 3
c1: 4
c2: 5
c1: 5
c2: 6
c2: 7
c1: 6
c1: 7
c2: 8
c1: 8
c1: 9
c1: 10
c2: 9
c2: 10
可以看到,c1
和 c2
的结果完全独立
c2h
static void c2h() throws InterruptedException {
Observable o = Observable.create(e -> {
Observable.interval(
10
, TimeUnit.MILLISECONDS
, Schedulers.computation()
)
.take(Integer.MAX_VALUE)
.subscribe(e::onNext);
})
.observeOn(Schedulers.newThread());
// publish 操作符,将 Cold Observable 转化为 Hot Observable
ConnectableObservable connectableObservable
= o.publish();
// 需要调用 connect 才开始执行
connectableObservable.connect();
connectableObservable.subscribe(t -> System.out.println("c1: " + t));
connectableObservable.subscribe(t -> System.out.println(" c2: " + t));
Thread.sleep(100);
connectableObservable.subscribe(t -> System.out.println(" c3: " + t));
Thread.sleep(100);
}
// 结果:
c1: 0
c2: 0
c1: 1
c2: 1
c1: 2
c2: 2
c1: 3
c2: 3
c1: 4
c2: 4
c1: 5
c2: 5
c1: 6
c2: 6
c1: 7
c2: 7
c1: 8
c2: 8
c1: 9
c2: 9
c1: 10
c2: 10
c1: 11
c2: 11
c3: 11
c1: 12
c2: 12
c3: 12
c1: 13
c2: 13
c3: 13
c1: 14
c2: 14
c3: 14
c1: 15
c2: 15
c3: 15
c1: 16
c2: 16
c3: 16
c1: 17
c2: 17
c3: 17
c1: 18
c2: 18
c3: 18
c1: 19
c2: 19
c3: 19
c1: 20
c2: 20
c3: 20
c1: 21
c2: 21
c3: 21
通过 publish
操作符,Cold Observable 可以转化为 Hot Observable,可以看到实例中的 订阅者 共享一个 Observable,c3
丢失了订阅之前的消息(广播电台不会因为你没打开收音机而为你补上之前的消息)
h2c
static void h2c() throws InterruptedException {
ConnectableObservable connectableObservable
= Observable.create(e -> {
Observable.interval(
10
, TimeUnit.MILLISECONDS
, Schedulers.computation()
)
.take(Integer.MAX_VALUE)
.subscribe(e::onNext);
})
.observeOn(Schedulers.newThread())
.publish();
connectableObservable.connect();
Observable o = connectableObservable.refCount();
/**
* Disposable:可以选择是否取消订阅
*/
Disposable d1 = o.subscribe(t -> System.out.println("c1: " + t));
Disposable d2 = o.subscribe(t -> System.out.println(" c2: " + t));
Thread.sleep(20);
/**
* 取消订阅
*/
d1.dispose();
d2.dispose();
System.out.println("重新发送");
o.subscribe(t -> System.out.println("c1: " + t));
o.subscribe(t -> System.out.println(" c2: " + t));
Thread.sleep(20);
}
// 结果:
c1: 0
c2: 0
c1: 1
c2: 1
c1: 2
c2: 2
重新发送
c1: 0
c2: 0
c1: 1
c2: 1
c1: 2
c2: 2
通过 refCount
操作符,Hot Observable 可以转化为 Cold Observable
同时,如果所有 订阅者 取消订阅,则数据流停止,重新订阅则开始重新发送。注意,是所有 订阅者 取消订阅,如果仅部分 订阅者 取消,则重新订阅并不会重新发送,如下示例:
static void h2c2() throws InterruptedException {
ConnectableObservable connectableObservable
= Observable.create(e -> {
Observable.interval(
10
, TimeUnit.MILLISECONDS
, Schedulers.computation()
)
.take(Integer.MAX_VALUE)
.subscribe(e::onNext);
})
.observeOn(Schedulers.newThread())
.publish();
connectableObservable.connect();
Observable o = connectableObservable.refCount();
/**
* Disposable:可以选择是否取消订阅
*/
Disposable d1 = o.subscribe(t -> System.out.println("c1: " + t));
Disposable d2 = o.subscribe(t -> System.out.println(" c2: " + t));
Thread.sleep(20);
/**
* 仅 d1 取消订阅
*/
d1.dispose();
// d2.dispose();
System.out.println("重新发送");
o.subscribe(t -> System.out.println("c1: " + t));
Thread.sleep(20);
}
// 结果:
c1: 0
c2: 0
c1: 1
c2: 1
c1: 2
c2: 2
重新发送
c2: 3
c1: 3
c2: 4
c1: 4
c2: 5
c1: 5
可以看到,因为 c2
并没有取消订阅,因此重新订阅后的 Observable 没有重新发送数据
Subject
Subject 有四种类型
AsyncSubject
Observer 会接受 AsyncSubject 的 onComplete
之前的最后一个数据
static void asyncSubject() {
AsyncSubject asyncSubject = AsyncSubject.create();
asyncSubject.onNext(1);
asyncSubject.onNext(2);
asyncSubject.subscribe(t -> System.out.println(t));
asyncSubject.onNext(3);
asyncSubject.onNext(4);
asyncSubject.onComplete();
}
// 4
BehaviorSubject
Observer 会先接收到 BehaviorSubject 被订阅之前的最后一个数据,在接受订阅之后发射的数据。如果被订阅之前没有发送任何数据,则会发送一个默认数据
static void behaviorSubject() {
BehaviorSubject behaviorSubject
= BehaviorSubject.createDefault("default");
// behaviorSubject.onNext(1);
behaviorSubject.subscribe(t -> System.out.println(t));
behaviorSubject.onNext(2);
}
// default 2
如果打开注释的代码,则结果为 1 2
,订阅前未发射数据则 Observer 接受到默认消息 default
ReplaySubject
ReplaySubject 可以发射所有来自原始 Observable 的数据,也可以选择只缓存订阅前的若干条消息,乃至缓存多长时间
// 发射所有消息
static void replaySubject1() {
ReplaySubject replaySubject = ReplaySubject.create();
replaySubject.onNext(1);
replaySubject.onNext(2);
replaySubject.subscribe(t -> System.out.println(t));
replaySubject.onNext(3);
}
// 1 2 3
// 缓存一条消息
static void replaySubject2() {
ReplaySubject replaySubject = ReplaySubject.createWithSize(1);
replaySubject.onNext(1);
replaySubject.onNext(2);
replaySubject.subscribe(t -> System.out.println(t));
replaySubject.onNext(3);
}
// 2 3
// 缓存 1s
static void replaySubject3() throws InterruptedException {
ReplaySubject replaySubject
= ReplaySubject.createWithTime(
1
, TimeUnit.SECONDS
, Schedulers.newThread()
);
replaySubject.onNext(1);
Thread.sleep(2000);
replaySubject.subscribe(t -> System.out.println(t));
replaySubject.onNext(3);
}
// 3
PublishSubject
Observer 只接受 PublishSubject 被订阅之后的数据
static void publishSubject() {
PublishSubject publishSubject = PublishSubject.create();
publishSubject.onNext(1);
publishSubject.onNext(2);
publishSubject.subscribe(t -> System.out.println(t));
publishSubject.onNext(3);
publishSubject.onNext(4);
}
// 3 4
总结
本章节介绍 Hot Observable & Cold Observable、Subject 相关概念以及 API,下一章节介绍 RxJava 的 操作符
下一篇:【从零学 RxJava】RxJava 2 —— 创建操作符
github
文中涉及的示例 demo
:
https://github.com/dangzhicairang/rxjava.git
参考
RxJava 2.x 实战 —— 沈哲