走进RxJava

149 阅读4分钟

一、RxJava是什么?

  1. RxJava是JVM对于响应式编程的一种实现,它是一个使用观察者模式组合异步和基于事件编程的java类库
  2. RxJava扩展观察者模式去支持一系列的数据或事件,并且允许你添加将多个序列聚合在一起的操作,同时不用关注底层线程、同步、线程安全、并发数据结构等问题(提供了Schedulers)
  3. 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的基类

  1. Flowable:0~n个流,支持响应流和背压
  2. Observable:0~n个流,不支持背压
  3. Single:一个流或者一个error
  4. Completable:一个完成或者错误信号流
  5. 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 组装时

  1. 所谓组装时也就是通过各种中间计算数据源的过程
  2. 下面这个过程就是没有数据流动,只是在计算数据源
Flowable<Integer> flow = Flowable.range(1, 5)
.map(v -> v * v)
.filter(v -> v % 3 == 0);

3.4 订阅时

  1. 当使用subscribe时就是订阅时间
  2. subscribe会触发doOnSubscribe的调用
flow.subscribe(System.out::println)

3.5 运行时

  1. 流主动发出数据、错误、完成信号的状态叫做运行时
  2. 下面代码是不断去创建流,当不为偶数时就抛出异常;其中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、简单的后台计算

  1. 下面例子是利用后台线程去执行任务,在前台线程展示结果或者错误
  2. RxJava使用的是建造者模式,每一个操作都会返回一个Flowable或者Observable,代码显得很简洁
  3. 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

  1. RxJava并不会和线程、线程池直接的联系,但是使用Schedulers的统一API来屏蔽并发,下面看一下RxJava3提供标准模式
  2. Schedulers.computation():CPU密集型计算,以固定的数量的线程在后台运行计算任务,大多数异步操作默认使用它进行计算
  3. Schedulers.io():io密集型的计算,在一个动态变化的线程上运行一个类似io或者阻塞操作
  4. Schedulers.single():在一个线程上运行任务,并且按照FIFO的模式
  5. Schedulers.trampoline():在一个参与的线程中以顺序和FIFO方式运行一个任务,通常用于测试场景

6、并发运行

6.1 map的顺序执行

  1. 在map中平方计算的操作是顺序执行的,并不是并发执行的
Flowable.range(1, 9)
            .subscribeOn(Schedulers.computation())
            .map(v -> v * v)
            .subscribe(System.out::println);

6.2 flatmap的并发执行

  1. flatmap能保证并发执行
  2. 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进行并发执行

  1. parallel将流转换成ParallelFlowable
  2. sequential将ParallelFlowable转换成Flowable,这样才能被subscribe消费
  3. 推荐使用这种方式进行并发执行
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 延迟依赖

  1. 延迟依赖表示当某个组装阶段的处理依赖上一个运行阶段的数据;下面就是延迟依赖的例子
  2. 延迟依赖可以使用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. 基类之间的转换

当需要在基类中进行转换时,参照下表

image