一、RxJava是什么?
- RxJava是JVM对于响应式编程的一种实现,它是一个使用观察者模式组合异步和基于事件编程的java类库
- RxJava扩展观察者模式去支持一系列的数据或事件,并且允许你添加将多个序列聚合在一起的操作,同时不用关注底层线程、同步、线程安全、并发数据结构等问题(提供了Schedulers)
- RxJava非常轻量级,它有多语种的实现,比如scala、Groovy、kotlin等
二、RxJava简介
1、maven依赖
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>3.0.4</version>
</dependency>
1.1 hello world
打印hello world
Flowable.just("hello world!").subscribe(System.out :: println);
2、RxJava3的基类
- Flowable:0~n个流,支持响应流和背压
- Observable:0~n个流,不支持背压
- Single:一个流或者一个error
- Completable:一个完成或者错误信号流
- Maybe:没有流或者一个流(error)
3、Rxjava术语
3.1 上下游
以operator2作为基点,左边到source都叫做上游,右边到consumer都叫做下游
source.operator1().operator2().operator3().subscribe(consumer);
3.2 数据对象
在RxJava把emission, emits, item, event, signal, data和message都认为是沿着流运动的对象
3.3 背压
背压是上游发送数据的速度大于当前节点消费数据的速率
3.3 组装时
- 所谓组装时也就是通过各种中间计算数据源的过程
- 下面这个过程就是没有数据流动,只是在计算数据源
Flowable<Integer> flow = Flowable.range(1, 5)
.map(v -> v * v)
.filter(v -> v % 3 == 0);
3.4 订阅时
- 当使用subscribe时就是订阅时间
- subscribe会触发doOnSubscribe的调用
flow.subscribe(System.out::println)
3.5 运行时
- 流主动发出数据、错误、完成信号的状态叫做运行时
- 下面代码是不断去创建流,当不为偶数时就抛出异常;其中onNext是将数据流向下游,error也会被下游等subscribe订阅到
Observable.create(emitter -> {
while (!emitter.isDisposed()) {
long time = System.currentTimeMillis();
emitter.onNext(time);
if (time % 2 != 0) {
emitter.onError(new IllegalStateException("Odd millisecond!"));
break;
}
}
})
.subscribe(System.out::println, Throwable::printStackTrace);
4、简单的后台计算
- 下面例子是利用后台线程去执行任务,在前台线程展示结果或者错误
- RxJava使用的是建造者模式,每一个操作都会返回一个Flowable或者Observable,代码显得很简洁
- Schedulers在下面具体分析
Flowable.fromCallable(() -> {
Thread.sleep(1000); // imitate expensive computation
return "Done";
})
.subscribeOn(Schedulers.io()) // 转换成后台线程执行
.observeOn(Schedulers.single()) // 当数据准备好使用前台获取结果
.subscribe(System.out::println, Throwable::printStackTrace);
Thread.sleep(2000); // <--- wait for the flow to finish
5、Schedulers
- RxJava并不会和线程、线程池直接的联系,但是使用Schedulers的统一API来屏蔽并发,下面看一下RxJava3提供标准模式
- Schedulers.computation():CPU密集型计算,以固定的数量的线程在后台运行计算任务,大多数异步操作默认使用它进行计算
- Schedulers.io():io密集型的计算,在一个动态变化的线程上运行一个类似io或者阻塞操作
- Schedulers.single():在一个线程上运行任务,并且按照FIFO的模式
- Schedulers.trampoline():在一个参与的线程中以顺序和FIFO方式运行一个任务,通常用于测试场景
6、并发运行
6.1 map的顺序执行
- 在map中平方计算的操作是顺序执行的,并不是并发执行的
Flowable.range(1, 9)
.subscribeOn(Schedulers.computation())
.map(v -> v * v)
.subscribe(System.out::println);
6.2 flatmap的并发执行
- flatmap能保证并发执行
- flatmap不能保证顺序,使用blockingSubscribe可以是顺序输出,可以使用subscribe来看下乱序的效果
Flowable.range(1, 9)
.flatMap(v -> Flowable.just(v).subscribeOn(Schedulers.computation()).map(w -> w * w))
.blockingSubscribe(System.out::println);
6.3 使用parallel进行并发执行
- parallel将流转换成ParallelFlowable
- sequential将ParallelFlowable转换成Flowable,这样才能被subscribe消费
- 推荐使用这种方式进行并发执行
Flowable.range(1, 9)
.parallel()
.runOn(Schedulers.computation())
.map(v -> v * v)
.sequential()
.subscribe(System.out::println);
7. 依赖处理
7.1 有依赖
比如某个service调用必须依赖上一个service结果,可以使用flatmap
service.apiCall()
.flatMap(value -> service.anotherApiCall(value))
.flatMap(next -> service.finalCall(next))
// 另外一种方式
service.apiCall()
.flatMap(value ->
service.anotherApiCall(value)
.flatMap(next -> service.finalCallBoth(value, next))
)
7.2 没有依赖
可以正常使用api
7.3 延迟依赖
- 延迟依赖表示当某个组装阶段的处理依赖上一个运行阶段的数据;下面就是延迟依赖的例子
- 延迟依赖可以使用Single.defer()来处理
AtomicInteger count = new AtomicInteger();
// 实现将count+10,并将结果打印出来
// 1. 这个打印出来的是0
Flowable.range(1,10)
.doOnNext(a -> count.incrementAndGet())
.ignoreElements()
.andThen(Single.just(count.get()))
.subscribe(System.out::println);
// 2. 这个打印出来的是20
Flowable.range(1,10)
.doOnNext(a -> count.incrementAndGet())
.ignoreElements()
.andThen(Single.defer(() -> Single.just(count.get())))
.subscribe(System.out::println);
8. 基类之间的转换
当需要在基类中进行转换时,参照下表