响应式编程系列二《rxjava》

响应式编程是什么,在上一篇规约中已经提到过,这里不再赘述。那接下来我们将深入检出的掌握RxJava。

目录

1. RxJava背景

2. 官方定义

3. 实现原理

3.1 基本实现步骤

3.2 Observer 和 Subscriber的区别

4. Rx1.x

4.1 事件流程

4.2 Sync & Async

4.3 操作符

4.4 背压

4.5 Scheduler

4.6 Hot & Cold

5. Rx2.x

5.1 Disposable

5.2 五种观察者模式

5.3 Observable & Observer

5.4 Flowable & Subscriber

5.5 Single & SingleObserver

5.6 Completable & CompletabeObserver

5.7 Maybe & MaybeObserver

6. 并行

6.1 Flowable.flatMap

6.2 ParallelFlowable


1. RxJava背景

ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET,社区网站是 reactivex.io。

主要版本:RxJava 1.x(官方已宣布停止维护),RxJava 2.x(全新的API)

2. 官方定义

微软给的定义是,Rx是一个函数库,让开发者可以利用可观察序列和LINQ风格查询操作符来编写异步和基于事件的程序,使用Rx,开发者可以用Observables表示异步数据流,用LINQ操作符查询异步数据流, 用Schedulers参数化异步数据流的并发处理,Rx可以这样定义:Rx = Observables + LINQ + Schedulers

ReactiveX.io给的定义是,Rx是一个使用可观察数据流进行异步编程的编程接口,ReactiveX结合了观察者模式迭代器模式函数式编程的精华。

RxJava给的定义是,Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。

总之:Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,响应式编程的解决方案、观察者设计模式、一个实现异步操作的库。

3. 实现原理

rxjava是通过扩展的观察者模式来实现的,Observable是异步的双向push,Iterable是同步单向pull。

3.1 基本实现步骤

基本实现通过3个步骤

1. 创建 Observer

Observer 即观察者,它决定事件触发的时候将有怎样的行为。除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的:

Observer observer = new Observer() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }
    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }
    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};
Subscriber subscriber = new Subscriber() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }
    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }
    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

不仅基本使用方式一样,实际上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。

2. 创建 Observable

Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。create() 方法是 RxJava 最基本的创造事件序列的方法。基于这个方法, RxJava 还提供了一些方法用来快捷创建事件队列,例如:

Observable observable = Observable.create(new Observable.OnSubscribe() {
    @Override
    public void call(Subscriber subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});
/**
* just(T...): 将传入的参数依次发送出来,将会依次调用:
* onNext("Hello");
* onNext("Hi");
* onNext("Aloha");
* onCompleted();
**/
Observable observable = Observable.just("Hello", "Hi", "Aloha");
/**
* from(T[]) / from(Iterable) : 将传入的数组或 Iterable 拆分成具体对象后,依次发送* 出来
* 将会依次调用:
* onNext("Hello");
* onNext("Hi");
* onNext("Aloha");
* onCompleted();
**/
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);

3. 订阅 Subscribe

创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了。

observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

subscriber() 做了3件事,另一篇文章中展开讲

3.2 Observer 和 Subscriber的区别

前面讲过,Observer最终会被转出Subscriber 再使用,故在使用上二者都可以,但也有本质的区别:

  • onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用。可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法,具体可以在后面的文中看到。
  • unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。 unsubscribe() 这个方法很重要,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。

4. Rx1.x

4.1 事件流程

被观察者(Observable)通过订阅(Subscribe)按顺序发送事件给观察者(Observer),观察者(Observer)按顺序接收事件&作出对应的响应动作,见下图

响应式编程系列二《rxjava》_第1张图片

其中订阅subscribe()的工作流程见下图:

方法调用顺序:观察者.onSubscribe()> 被观察者.subscribe()> 观察者.onNext()>观察者.onComplete()

响应式编程系列二《rxjava》_第2张图片

4.2 Sync & Async

Observable通过使用最佳的方式访问异步数据序列填补了这个间隙

 

单个数据

多个数据

同步

T getData()

Iterable getData()

异步

Future getData()

Observable getData()

4.3 操作符

Observable和Observer仅仅是个开始,它们本身不过是标准观察者模式的一些轻量级扩展,目的是为了更好的处理事件序列。Rx的操作符让你可以用声明式的风格组合异步操作序列,它拥有回调的所有效率优势,同时又避免了典型的异步系统中嵌套回调的缺点。

  1. 创建操作 Create, Defer, Empty/Never/Throw, From, Interval, Just, Range, Repeat, Start, Timer
  2. 变换操作 Buffer, FlatMap, GroupBy, Map, Scan和Window
  3. 过滤操作 Debounce, Distinct, ElementAt, Filter, First, IgnoreElements, Last, Sample, Skip, SkipLast, Take, TakeLast
  4. 组合操作 And/Then/When, CombineLatest, Join, Merge, StartWith, Switch, Zip
  5. 错误处理 Catch和Retry
  6. 辅助操作 Delay, Do, Materialize/Dematerialize, ObserveOn, Serialize, Subscribe, SubscribeOn, TimeInterval, Timeout, Timestamp, Using
  7. 条件和布尔操作 All, Amb, Contains, DefaultIfEmpty, SequenceEqual, SkipUntil, SkipWhile, TakeUntil, TakeWhile
  8. 算术和集合操作 Average, Concat, Count, Max, Min, Reduce, Sum
  9. 转换操作 To
  10. 连接操作 Connect, Publish, RefCount, Replay
  11. 反压操作,用于增加特殊的流程控制策略的操作符

4.4 背压

背压(Backpressure)是指在异步场景中,被观察者发送事件速度远远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。(即背压是一种控制流速的策略,前提是异步,就是被观察者跟观察者处在不同的线程环境中)。

Backpressure  指的是在 Buffer 有上限的系统中,Buffer 溢出的现象;它的应对措施只有一个:丢弃新事件。

背压策略的实现方式(即响应式拉取:reactive pull

在Rxjava的观察者模型中,被观察者是主动的推动数据给观察者的,而观察者是被动接收的,而响应式则反过来,观察者主动从被观察者那里去拉取数据,而被观察者则变成被动的等待通知再发送数据,即观察者可以根据自身实际情况按需拉取数据,而不是被动接收(这就可以达到告诉被观察者把速度慢下来),从而实现对被观察者发送事件速度的控制,从而实现背压。总结起来就是:背压是一种策略,具体措施是下游观察者通知上游的被观察者发送事件,背压策略很好的解决了异步环境下被观察者和观察者速度不一致的问题。

在Rxjava1.x中,关于背压都是集中在Observable这个类中,导致有的Observable支持背压,有的不支持,Rxjava2.0为了解决这种缺憾,把支持背压跟不支持背压的Observable区分开来。

  1. Observable ( 被观察者 ) / Observer ( 观察者 )

响应式编程系列二《rxjava》_第3张图片

  1. Flowable (被观察者)/ Subscriber (观察者)

Flowable背压策略

描述

BackpressureStrategy.ERROR

缓存区默人大小128,流速不均衡时发射MissingBackpressureException信号

BackpressureStrategy.BUFFER

缓存区不限制大小,使用不当仍会OOM

BackpressureStrategy.DROP

缓存最近的nNext事件

BackpressureStrategy.LATEST

缓存区会保留最后的OnNext事件,覆盖之前缓存的OnNext事件

BackpressureStrategy.MISSING

OnNext事件没有任何缓存和丢弃,下游要处理任何溢出

由Observer变成了Subscriber,主要区别是增加了void request(long n)方法;

由ObservableEmitter变成FlowableEmitter,主要区别是增加了 long requested();

响应式编程系列二《rxjava》_第4张图片

参考实例

4.5 Scheduler

在 RxJava 的默认规则(在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件),事件的发出和消费都是在同一个线程的。

如果你想给Observable操作符链添加多线程功能,你可以指定操作符在特定的调度器(Scheduler)上执行。

Observable.create(new ObservableOnSubscribe() {
    @Override
    public void subscribe(@NonNull ObservableEmitter subscriber) throws Exception {
        System.out.println("create -  当前线程信息:" + Thread.currentThread().getName());
        subscriber.onNext(20);
        subscriber.onNext(3);
        subscriber.onNext(3);
        subscriber.onComplete();
    }
}).subscribeOn(Schedulers.computation()).observeOn(Schedulers.io())
        .subscribe(new Observer() {
            @Override
            public void onSubscribe(@NonNull Disposable disposable) {
                System.out.println("onSubscribe - 当前线程信息:" + Thread.currentThread().getName());
            }

            @Override
            public void onNext(@NonNull Integer integer) {
                System.out.println("onNext - 当前线程信息:" + Thread.currentThread().getName());
            }

            @Override
            public void onError(@NonNull Throwable throwable) {
                System.out.println("onError - 当前线程信息:" + Thread.currentThread().getName());
            }

            @Override
            public void onComplete() {
                System.out.println("onComplete - 当前线程信息:" + Thread.currentThread().getName());
            }
        });
/**
onSubscribe - 当前线程信息:main
create -  当前线程信息:RxComputationThreadPool-1
onNext - 当前线程信息:RxCachedThreadScheduler-1
onNext - 当前线程信息:RxCachedThreadScheduler-1
onNext - 当前线程信息:RxCachedThreadScheduler-1
onComplete - 当前线程信息:RxCachedThreadScheduler-1
**/

SubscribeOn操作符

叫事件产生的线程,它指示Observable将全部的处理过程(包括发射数据和通知)放在特定的调度器上执行,即 Observable.OnSubscribe 被激活时所处的线程。

指定 subscribeOn(Schedulers.io()) 时,被创建的事件的内容 1234 将会在 IO 线程发出。

ObserveOn操作符

叫事件消费的线程,它指示Observer在一个特定的调度器上执行观察者的onNext,onError和onCompleted方法,指定 Subscriber 所运行在的线程。

具体的原则:

  • 事件产生:默认运行在当前线程,可以由 subscribeOn()自定义线程
  • 事件加工:默认跟事件产生的线程保持一致, 可以由 observeOn()自定义线程
  • 事件消费:默认运行在当前线程,可以有observeOn()自定义
  • 如果只规定了事件产生的线程,那么事件消费线程(包含事件加工)将跟随事件产生线程。
  • 如果只规定了事件消费的线程,那么事件产生的线程和当前线程保持一致。

执行流程

响应式编程系列二《rxjava》_第5张图片

注:上图画的不准确,map前缺失observeOn

调度器的种类

调度器类型

效果

Schedulers.immediate( )

直接在当前线程运行,相当于不指定线程。这是默认的

Schedulers.newThread( )

总是启用新线程,并在新线程执行操作

Schedulers.io( )

用于IO密集型任务,如异步阻塞IO操作(读写文件、读写数据库、网络信息交互等),行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池(线程池会根据需要增长),可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程,对于普通的计算任务,请使用Schedulers.computation()。Schedulers.io( )默认是一个CachedThreadScheduler,很像一个有线程缓存的新线程调度器

Schedulers.computation( )

用于计算任务,使用的固定的线程池,大小为 CPU 核数。 这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算,事件循环或和回调处理。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。

Schedulers.from(executor)

使用指定的Executor作为调度器

延时和周期调度器

someScheduler.schedule(someAction, 500, TimeUnit.MILLISECONDS);//500毫秒之后开始执行;

someScheduler.schedulePeriodically(someAction, 500, 250, TimeUnit.MILLISECONDS);//任务将在500毫秒之后执行,然后每250毫秒执行一次;

小结阻塞&非阻塞

  • 从阻塞到非阻塞模式:subscribeOn
  • 从非阻塞到阻塞模式:blocking* 相关操作符,比如可以通过blockingSubscribe 变为在主线程上消费

4.6 Hot & Cold

理解冷热两种模式下的Observables对于掌握Observables来说至关重要,我们先来读一读RxJS官方的定义:

Cold Observables在被订阅后运行,也就是说,observables序列仅在subscribe函数被调用后才会推送数据。与Hot Observables不同之处在于,Hot Observables在被订阅之前就已经开始产生数据,例如mouse move事件

  • Cold Observable在每个订阅者订阅的时候都独立的执行一遍数据流代码,Observable.interval 就是一个 Cold Observable。
  • Hot observable 不管有没有订阅者订阅,他们创建后就开发发射数据流。 一个比较好的示例就是 鼠标事件。 不管系统有没有订阅者监听鼠标事件,鼠标事件一直在发生,当有订阅者订阅后,从订阅后的事件开始发送给这个订阅者,之前的事件这个订阅者是接受不到的;如果订阅者取消订阅了,鼠标事件依然继续发射。
let obs = Rx.Observable.create(observer => observer.next(1));
obs.subscribe(v => console.log("1st subscriber: " + v));
obs.subscribe(v => console.log("2nd subscriber: " + v));
//运行结果:
1st subscriber: 1
2nd subscriber: 1

那问题来了,代码中的obs是冷模式还是热模式?

分析上面官方的解释,如果obs是冷模式,那么它被订阅后才会产生“新鲜”的数据。为了体现“新鲜”这一点,我们用Date.now()来替代数字1, 我们注意到两次获得的数据并不相同,这意味着observer.next(Date.now())一定被调用了两次。

那该怎么破?

当然冷模式是可以转换为热模式的,使用ConnectableFlowable & publish & connect。

  • ConnectableFlowable 可连接的Flowable, 不管是否消费,只有调用了connect, 数据就一直在发射,不受消费影响 ('冷' 的Flowable 变成'热'的)
  • publish 将 普通 Flowable,变成 ConnectableFlowable ;
let obs = Rx.Observable
            .create(observer => observer.next(Date.now()))
            .publish();
obs.subscribe(v => console.log("1st subscriber: " + v));
obs.subscribe(v => console.log("2nd subscriber: " + v));
obs.connect();
publish

响应式编程系列二《rxjava》_第6张图片

publish 返回一个 ConnectableObservable 对象,它对于数据源共享同一个订阅,但还没有订阅到数据源(像一个守门员,保证所有的订阅都订阅到ConnectableObservable上面,而不是数据源本身),它是 Observable 的拓展,多了三个函数:

public final Subscription connect()
public abstract void connect(Action1 connection)
public Observable refCount()

connect connect操作符使ConnectableObservable实际订阅到数据源(如果不调用connect 函数则不会触发数据流的执行)。当调用 connect 函数以后,会创建一个新的subscription 并订阅到源 Observable,这个 subscription 开始接收数据并把它接收到的数据转发给所有的订阅者。这样,所有的订阅者在同一时刻都可以收到同样的数据。
refCount

refCount 返回一个特殊的 Observable,这个 Observable 只要有订阅者就会继续发射数据,实际上对ConnectableObservable的所有订阅进行了记录。

Observable cold = Observable.interval(200, TimeUnit.MILLISECONDS).publish().refCount();

replay

replay 将Flowable变成 ConnectableFlowable, 在connect之后,确保每次消费都使用相同数据(会保存历史数据进行回放)。

如何使用?

这儿有一条经验:当你有一个冷模式的Observable而又想不同的订阅者订阅它时获得之前产生过的数据时,你可以使用publish和它的小伙伴们。

5. Rx2.x

5.1 Disposable

这个东西可以直接调用切断,可以看到,当它的 isDisposed() 返回为 false 的时候,接收器能正常接收事件,但当其为 true 的时候,接收器停止了接收。所以可以通过此参数动态控制接收事件了。

5.2 五种观察者模式

Observable & Observer

见下

Flowable & Subscriber

见下

Single & SingleObserver

见下

Completable & CompletabeObserver

见下

Maybe & MaybeObserver

见下

5.3 Observable & Observer

//基本用法与v1.x类似,略
Observable.create(new ObservableOnSubscribe() {
            // 1. 创建被观察者 & 生产事件
            @Override
            public void subscribe(ObservableEmitter emitter) throws Exception {
                System.out.println("subscribe传递");
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                emitter.onComplete();
            }
        }).subscribe(new Observer() {
            // 2. 通过通过订阅(subscribe)连接观察者和被观察者
            // 3. 创建观察者 & 定义响应事件的行为
            @Override
            public void onSubscribe(Disposable d) {
                System.out.println("开始采用subscribe连接");
            }
            // 默认最先调用复写的 onSubscribe()
            @Override
            public void onNext(Integer value) {
                System.out.println("对Next事件" + value + "作出响应");
            }
            @Override
            public void onError(Throwable e) {
                System.out.println("对Error事件作出响应");
            }
            @Override
            public void onComplete() {
                System.out.println("对Complete事件作出响应");
            }
        });

5.4 Flowable & Subscriber

//大部分用法与Observable类似,多数情况下使用背压时需要它,
Flowable.create(new FlowableOnSubscribe() {
    @Override
    public void subscribe(@NonNull FlowableEmitter e) throws Exception {
        int i = 0;
        while(i < Long.MAX_VALUE){
            e.onNext(i);
            i++;
        }
    }
}, BackpressureStrategy.DROP)
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.newThread())
        .subscribe(new Subscriber() {
            @Override
            public void onSubscribe(Subscription s) {
                s.request(Long.MAX_VALUE);
            }
            @Override
            public void onNext(Integer integer) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("Test","i = "+integer);
            }
            @Override
            public void onError(Throwable t) {
            }
            @Override
            public void onComplete() {
            }
        });

5.5 Single & SingleObserver

只发射一条单一的数据,或者一条异常通知,不能发射完成通知,其中数据与通知只能发射一个。

Single.create(new SingleOnSubscribe() {
            @Override
            public void subscribe(@NonNull SingleEmitter singleEmitter) throws Exception {
                singleEmitter.onSuccess("hello");
                //singleEmitter.onError(new Exception("测试异常"));
            }
        }).subscribe(new SingleObserver() {
            @Override
            public void onSubscribe(@NonNull Disposable disposable) {
            }
            @Override
            public void onSuccess(@NonNull Object o) {
                System.out.println("onSuccess");
            }
            @Override
            public void onError(@NonNull Throwable throwable) {
                System.out.println("onError");
            }
        });

5.6 Completable & CompletabeObserver

只发射一条完成通知,或者一条异常通知,不能发射数据,其中完成通知与异常通知只能发射一个

Completable.create(new CompletableOnSubscribe() {
            @Override
            public void subscribe(@NonNull CompletableEmitter completableEmitter) throws Exception {
                //completableEmitter.onComplete();
                completableEmitter.onError(new Exception("测试异常"));
            }
        }).subscribe(new CompletableObserver() {
            @Override
            public void onSubscribe(@NonNull Disposable disposable) {
            }
            @Override
            public void onComplete() {
                System.out.println("onComplete");
            }
            @Override
            public void onError(@NonNull Throwable throwable) {
                System.out.println("onError");
            }
        });

5.7 Maybe & MaybeObserver

可发射一条单一的数据,以及发射一条完成通知,或者一条异常通知,其中完成通知和异常通知只能发射一个,发射数据只能在发射完成通知或者异常通知之前,否则发射数据无效。

Maybe.create(new MaybeOnSubscribe() {
            @Override
            public void subscribe(@NonNull MaybeEmitter maybeEmitter) throws Exception {
                //maybeEmitter.onSuccess("suc");
                maybeEmitter.onError(new Exception("测试异常"));
                //maybeEmitter.onComplete();
            }
        }).subscribe(new MaybeObserver() {
            @Override
            public void onSubscribe(@NonNull Disposable disposable) {
            }
            @Override
            public void onSuccess(@NonNull String s) {
                System.out.println("onSuccess");
            }
            @Override
            public void onError(@NonNull Throwable throwable) {
                System.out.println("onError");
            }
            @Override
            public void onComplete() {
                System.out.println("onComplete");
            }
        });

6. 并行

6.1 Flowable.flatMap

RxJava的线程模型中被观察者(Observable、Flowable...)发射的数据流可以经历各种线程切换,但是数据流的各个元素之间不会产生并行执行的效果。我们知道并行并不是并发,不是同步,更不是异步。

Java8中有parallelStream(),如果要达到这个效果可以借着flatMap实现(每个Observable可以使用线程池来并发的执行)

Observable.range(1, 100).flatMap(new Function>() {
   @Override
   public ObservableSource apply(Integer integer) throws Exception {
      return Observable.just(integer).subscribeOn(Schedulers.computation()).map(new Function() {
        @Override
        public String apply(Integer integer) throws Exception {
            return integer.toString();
        }
      });
   }
}).subscribe(new Consumer() {
    @Override
    public void accept(String str) throws Exception {
        System.out.println(str);
    }
});

flatMap操作符的原理是将这个Observable转化为多个以原Observable发射的数据作为源数据的Observable,然后再将这多个Observable发射的数据整合发射出来,需要注意的是最后的顺序可能会交错地发射出来。

int threadNum = Runtime.getRuntime().availableProcessors() + 1;
final ExecutorService executor = Executors.newFixedThreadPool(threadNum);
final Scheduler scheduler = Schedulers.from(executor);
Observable.range(1, 20).flatMap(new Function>() {
    @Override
    public ObservableSource apply(Integer integer) throws Exception {
        return Observable.just(integer).subscribeOn(scheduler).map(new Function() {
            @Override
            public String apply(Integer integer) throws Exception {
                return integer.toString();
            }
        });
    }
}).doFinally(new Action() {//会在onError或者onComplete之后调用
    @Override
    public void run() throws Exception {
        executor.shutdown();
    }
}).subscribe(new Consumer() {
    @Override
    public void accept(String str) throws Exception {
        System.out.println(str);
    }
});

6.2 ParallelFlowable

类似Java 8的并行流,在相应的操作符上调用Flowable的parallel()就会返回ParallelFlowable。

ParallelFlowable parallelFlowable = Flowable.range(1, 100).parallel();
        parallelFlowable.runOn(Schedulers.io()).map(new Function() {
            @Override
            public Object apply(@NonNull Integer integer) throws Exception {
                return integer.toString();
            }
        }).sequential().subscribe(new Consumer() {
            @Override
            public void accept(@NonNull String str) throws Exception {
                System.out.println(str);
            }
        });

ParallelFlowable遵循与Flowable相同的异步原理,因此parallel()本身不引入顺序源的异步消耗,只准备并行流。但是可以通过runOn(可以指定prefetch的数量)操作符定义异步。这一点跟Flowable很大不同,Flowable是使用subscribeOn、observeOn操作符。

目前ParallelFlowable只支持如下的操作:
map,
filter,
flatMap,
concatMap,
reduce,
collect,
sort,
toSortedList,
compose,
doOnCancel, doOnError, doOnComplete, doOnNext, doOnSubscribe, doAfterTerminate, doOnRequest

那ParallelFlowable和Flowable.flatMap哪个更好呢?

其实实现并行的原理是一样的,RxJava 本质上是连续的,借助flatMap操作符进行分离和加入一个序列可能会变得复杂,并引起一定的开销。 但是如果使用ParallelFlowable的话开销会更小。

附录 Rxx中文翻译

附录Reactor指南中文版

 

你可能感兴趣的:(Java,#,规约)