RxJava简述

简介

  1. RxJava

https://github.com/ReactiveX/RxJava

RxJava is a Java VM implementation of Reactive Extensions (ReactiveX): a library for composing asynchronous and event-based programs by using observable sequences.

It extends the observer pattern to support sequences of data/events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety and concurrent data structures.

  1. ReactiveX

http://reactivex.io/

An API for asynchronous programming with observable streams

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

It extends the observer pattern to support sequences of data and/or events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety, concurrent data structures, and non-blocking I/O.

It is sometimes called “functional reactive programming” but this is a misnomer. ReactiveX may be functional, and it may be reactive, but “functional reactive programming” is a different animal. One main point of difference is that functional reactive programming operates on values that change continuously over time, while ReactiveX operates on discrete values that are emitted over time.

版本

  1. Version 1.x

1.x 版本已于2018年3月31日终止生命。将不会进行进一步的开发,支持,维护,PR和更新。

  1. Version 2.x

2.x 版本目前将不会在添加新功能,到2020年12月31日停止维护。

    <dependency>
        <groupId>io.reactivex.rxjava2</groupId>
        <artifactId>rxjava</artifactId>
        <version>2.2.12</version>
    </dependency>
  1. Version 3.x

目前最新的可用版本为 3.0.0.RC3

    <dependency>
        <groupId>io.reactivex.rxjava3</groupId>
        <artifactId>rxjava</artifactId>
        <version>3.0.0-RC3</version>
    </dependency>

2.x 版本 与 3.x 版本的 包名不兼容,升级时需注意。

基础概念

1. 基类

RxJava 3 features several base classes you can discover operators on:

  • io.reactivex.rxjava3.core.Flowable: 0…N flows, supporting Reactive-Streams and backpressure
  • io.reactivex.rxjava3.core.Observable: 0…N flows, no backpressure,
  • io.reactivex.rxjava3.core.Single: a flow of exactly 1 item or an error,(正好有1个项目的流或一个错误)
  • io.reactivex.rxjava3.core.Completable: a flow without items but only a completion or error signal,(没有项目但只有完成错误信号的流程,)
  • io.reactivex.rxjava3.core.Maybe: a flow with no items, exactly one item or an error.(没有项目,恰好一个项目或错误的流程)

2. 一些术语

上游、下游

RxJava中的数据流 包括一个源零个或多个中间步骤,然后是一个数据使用者或组合器步骤(其中,该步骤负责通过某种方式消耗数据流):

source.operator1().operator2().operator3().subscribe(consumer);

source.flatMap(value -> source.operator1().operator2().operator3());

在这里,如果我们想象自己在operator2上,向源的左侧看,则称为上游。向右看向订户/消费者的称为下游

Objects in motion

在RxJava的文档中,emission, emits, item, event, signal, data and message 被视为同义词,并表示沿数据流传播的对象。

Backpressure

当数据流通过异步步骤运行时,每个步骤可能以不同的速度执行不同的操作。为避免过多的步骤(通常会因临时缓冲或需要跳过/删除数据而导致内存使用量增加)而采用了所谓的背压,这是流控制的一种形式,其中的步骤可以表示多少项目准备好了吗?在通常没有步骤知道上游将发送给它多少项的情况下,这可以限制数据流的内存使用。

Assembly time

通过应用各种中间运算符来准备数据流发生在所谓的 Assembly time
在这期间,数据还没有流动,也没有副作用发生。

Flowable<Integer> flow = Flowable.range(1, 10)
        .map(v -> v * v)
        .filter(v -> v % 2 == 0);
//flow.subscribe(System.out::println);
Subscription time (订阅时间)

当在内部建立处理步骤链的流上调用subscribe()时,这是一个临时状态:

flow.subscribe(System.out::println);

当触发订阅副作用时(如 doOnSubscribe),在此状态下,某些来源会立即 block 或 emitting items .

Runtime

这是流主动发出项目,错误或完成信号时的状态:

    Observable.create(emitter -> {
        while (!emitter.isDisposed()) {
            long time = System.currentTimeMillis();
            emitter.onNext(time);
            if (time % 11 == 0) {
                emitter.onError(new IllegalStateException("Odd millisecond!"));
                break;
            }
        }
    }).subscribe(System.out::println, Throwable::printStackTrace);

实际上,这是上面给定示例的主体执行时的情况。

3. fluent API

    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

4. Schedulers

RxJava运算符不能直接与 Thread 或 ExecutorService 一起使用,但是使用所谓的 Scheduler,可以在统一的API之后抽象出并发源
RxJava 3 具有几个可通过 Schedulers 实用程序类访问的标准调度程序。

  • Schedulers.computation(): 在后台在固定数量的专用线程上运行计算密集型工作。大多数异步运算符将其用作其默认调度程序
  • Schedulers.io(): 在一组动态变化的线程上运行类似 I/O 或阻塞的操作。
  • Schedulers.single(): 以顺序和FIFO方式在单个线程上运行工作。
  • Schedulers.trampoline(): 通常在测试中以参与线程之一的顺序和 FIFO 方式运行工作。

此外,还可以通过 Schedulers.from(Executor) 将现有的 Executor(及其子类型,如ExecutorService)包装成 Scheduler

Observable.just("java", "C++", "python")
        //.observeOn(Schedulers.io())
    .observeOn(Schedulers.from(Executors.newFixedThreadPool(5)))
    .subscribe(System.out::println);

Concurrency within a flow

RxJava 中的流本质上是顺序的,分为多个处理阶段,这些阶段可以彼此并行运行:

Flowable.range(1, 10)
  .observeOn(Schedulers.computation())
  .map(v -> v * v)
  .blockingSubscribe(System.out::println);

此示例流在计算调度程序上对从1到10的数字进行平方运算,并在“主”线程(更精确地说,是BlockingSubscribe 的调用者线程)上使用结果。但是,lambda 表达式 v -> v * v 不会并行运行。它在一个计算线程中一个接一个地接收值1到10。

Parallel processing

并行处理数字1到10涉及到更多:

Flowable.range(1, 10)
  .flatMap(v ->
      Flowable.just(v)
        .subscribeOn(Schedulers.computation())
        .map(w -> w * w)
  )
  .blockingSubscribe(System.out::println);

实际上,RxJava中的并行性意味着运行独立的流并将其结果合并回单个流。

但是请注意,flatMap不保证任何顺序,内部流的最终结果可能会交错。还有其他运算符:

  • concatMap 一次映射并运行一个内部流,
  • concatMapEager 它将“一次”运行所有内部流,但输出流将按照创建这些内部流的顺序进行。

另外,Flowable.parallel() 运算符和 ParallelFlowable 类型有助于实现相同的并行处理模式:

Flowable.range(1, 10)
  .parallel()
  .runOn(Schedulers.computation())
  .map(v -> v * v)
  .sequential()
  .blockingSubscribe(System.out::println);

Dependent sub-flows

flatMap是功能强大的运算符,可在许多情况下提供帮助。例如,给定一个返回Flowable的服务,我们想调用第一个服务发出的值的另一个服务:

Flowable<Inventory> inventorySource = warehouse.getInventoryAsync();

inventorySource
    .flatMap(inventoryItem -> erp.getDemandAsync(inventoryItem.getId())
            .map(demand -> "Item " + inventoryItem.getName() + " has demand " + demand))
    .subscribe(System.out::println);

Continuations (延续)

有时,当某项变得可用时,人们希望对其进行一些相关的计算。有时将其称为 Continuations ,并且根据发生的情况和涉及的类型,可能涉及各种操作员来完成。

Dependent

最典型的情况是给一个值,调用另一个服务,等待并继续其结果:

service.apiCall()
.flatMap(value -> service.anotherApiCall(value))
.flatMap(next -> service.finalCall(next))

同样经常的情况是,后面的序列将需要来自前面的映射的值。这可以通过将外部 flatMap 移到以前的 flatMap 的内部来实现,例如:

service.apiCall()
.flatMap(value ->
    service.anotherApiCall(value)
    .flatMap(next -> service.finalCallBoth(value, next))
)

此处,原始值将在 lambda 变量捕获的帮助下在内部 flatMap 中提供。

Non-dependent 非依赖

In other scenarios, the result(s) of the first source/dataflow is irrelevant and one would like to continue with a quasi independent another source. Here, flatMap works as well:

Observable continued = sourceObservable.flatMapSingle(ignored -> someSingleSource)
continued.map(v -> v.toString())
  .subscribe(System.out::println, Throwable::printStackTrace);

however, the continuation in this case stays Observable instead of the likely more appropriate Single. (This is understandable because from the perspective of flatMapSingle, sourceObservable is a multi-valued source and thus the mapping may result in multiple values as well).

Often though there is a way that is somewhat more expressive (and also lower overhead) by using Completable as the mediator and its operator andThen to resume with something else:

sourceObservable
  .ignoreElements()           // returns Completable
  .andThen(someSingleSource)
  .map(v -> v.toString())

The only dependency between the sourceObservable and the someSingleSource is that the former should complete normally in order for the latter to be consumed.

Deferred-dependent 递延依附

Sometimes, there is an implicit data dependency between the previous sequence and the new sequence that, for some reason, was not flowing through the “regular channels”. One would be inclined to write such continuations as follows:

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.just(count.get()))
  .subscribe(System.out::println);

Unfortunately, this prints 0 because Single.just(count.get()) is evaluated at assembly time when the dataflow hasn’t even run yet. We need something that defers the evaluation of this Single source until runtime when the main source completes:

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.defer(() -> Single.just(count.get())))
  .subscribe(System.out::println);

or

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.fromCallable(() -> count.get()))
  .subscribe(System.out::println);

类型转换

Sometimes, a source or service returns a different type than the flow that is supposed to work with it.
In such situations, there are usually two options to fix the transformation: 1) convert to the desired type or 2) find and use an overload of the specific operator supporting the different type.

转换为所需的类型

每个反应式基类都具有可以执行此类转换(包括协议转换)以匹配某些其他类型的运算符。以下矩阵显示了可用的转换选项:
在这里插入图片描述
1: When turning a multi-valued source into a single valued source, one should decide which of the many source values should be considered as the result.

2: Turning an Observable into Flowable requires an additional decision: what to do with the potential unconstrained flow of the source Observable? There are several strategies available (such as buffering, dropping, keeping the latest) via the BackpressureStrategy parameter or via standard Flowable operators such as onBackpressureBuffer, onBackpressureDrop, onBackpressureLatest which also allow further customization of the backpressure behavior.

3: When there is only (at most) one source item, there is no problem with backpressure as it can be always stored until the downstream is ready to consume.

使用所需类型的重载

许多经常使用的运算符具有可以处理其他类型的重载。这些通常以目标类型的后缀命名:
在这里插入图片描述
The reason these operators have a suffix instead of simply having the same name with different signature is type erasure. Java doesn’t consider signatures such as operator(Function<T, Single>) and operator(Function<T, Maybe>) different (unlike C#) and due to erasure, the two operators would end up as duplicate methods with the same signature.

运算符命名约定

无法使用的关键字

在原始 Rx.NET 中,发出单个项目然后完成的运算符称为 Return(T)。由于Java约定要在方法名称前使用小写字母,因此它应该是 return(T),这是 Java 中的关键字,因此不可用。因此,RxJava 选择将此运算符命名为 just(T)。操作符 Switch 存在相同的限制,必须将其命名为 switchOnNext。另一个例子是 Catch,它名为onErrorResumeNext。

类型擦除

许多期望用户提供返回返回类型的函数的运算符不会重载,因为围绕 Function <T,X> 的类型擦除会将此类方法签名转换为重复项。RxJava 选择通过在类型后面加上后缀来命名此类运算符:

Flowable<R> flatMap(Function<? super T, ? extends Publisher<? extends R>> mapper)

Flowable<R> flatMapMaybe(Function<? super T, ? extends MaybeSource<? extends R>> mapper)
类型歧义

即使某些运算符在类型擦除方面没有问题,但它们的签名可能会变得模棱两可,特别是如果使用Java 8和lambda。例如,concatWith的一些重载以各种其他反应式基本类型作为参数(以在基础实现中提供便利和性能优势):

Flowable<T> concatWith(Publisher<? extends T> other);

Flowable<T> concatWith(SingleSource<? extends T> other);

Publisher 和 SingleSource 都显示为功能接口(一种抽象方法的类型),并可能鼓励用户尝试提供 lambda 表达式:

someSource.concatWith(s -> Single.just(2))
.subscribe(System.out::println, Throwable::printStackTrace);

不幸的是,这种方法不起作用,该示例根本无法打印2。实际上,从2.1.10版本开始,它甚至没有编译,因为至少存在4个concatWith重载,并且编译器发现上面的代码含糊不清。

在这种情况下,用户可能希望将某些计算推迟到someSource完成之前,因此应该将正确的明确运算符推迟:

someSource.concatWith(Single.defer(() -> Single.just(2)))
.subscribe(System.out::println, Throwable::printStackTrace);

有时,添加一个后缀以避免逻辑歧义可能会编译但在流中产生错误的类型:

Flowable<T> merge(Publisher<? extends Publisher<? extends T>> sources);

Flowable<T> mergeArray(Publisher<? extends T>... sources);

当功能接口类型作为类型参数T涉及时,这也可能变得模棱两可。

Error handling

数据流可能会失败,这时会将错误发送给使用者。但是有时候,多个来源可能会失败,这时可以选择是否等待所有来源完成或失败。为了表明这种机会,许多运算符名称后缀了DelayError字(而其他运算符在其重载之一中具有delayError或delayErrors布尔标志):

Flowable<T> concat(Publisher<? extends Publisher<? extends T>> sources);

Flowable<T> concatDelayError(Publisher<? extends Publisher<? extends T>> sources);

当然,各种后缀可能会一起出现:

Flowable<T> concatArrayEagerDelayError(Publisher<? extends T>... sources);
基类与基本类型

由于基类上大量的静态方法和实例方法,因此可以认为它们很重。RxJava 3的设计在很大程度上受Reactive Streams规范的影响,因此,该库为每个反应类型提供了一个类和一个接口:
在这里插入图片描述
1The org.reactivestreams.Publisher is part of the external Reactive Streams library. It is the main type to interact with other reactive libraries through a standardized mechanism governed by the Reactive Streams specification.

2The naming convention of the interface was to append Source to the semi-traditional class name. There is no FlowableSource since Publisher is provided by the Reactive Streams library (and subtyping it wouldn’t have helped with interoperation either). These interfaces are, however, not standard in the sense of the Reactive Streams specification and are currently RxJava specific only.

Versioning

@Beta

在类或方法级别用@Beta批注标记的API可能会发生更改。它们可以随时以任何方式修改,甚至删除。如果您的代码本身是一个库,则除非重新打包它们,否则不应使用beta API。

@Experimental

在类或方法级别上标有@Experimental批注的API几乎肯定会发生变化。它们可以随时以任何方式修改,甚至删除。您不应在任何生产代码中使用或依赖它们。它们纯粹是为了进行广泛的测试和反馈。

@Deprecated

在类或方法级别标有@Deprecated批注的API将一直受支持,直到下一个主要版本发布,但建议停止使用它们。

io.reactivex.rxjava3.internal.*

io.reactivex.rxjava3.internal.*包中的所有代码均被视为私有API,因此完全不应依赖。它可以随时更改。

– 资料来源:https://github.com/ReactiveX/RxJava#rxjava-reactive-extensions-for-the-jvm

https://mcxiaoke.gitbooks.io/rxdocs/content/Operators.html

https://github.com/lzyzsd/Awesome-RxJava

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值