Rxjava

RxJava 简介

RxJava 是 ReactiveX 在 Java 上的开源的实现。Observable(被观察者) 和 Subscriber(订阅者)是两个主要的类。在 RxJava 上,一个 Observable 是一个发出数据流或者事件的类,Subscriber 是一个对这些发出的 items (数据流或者事件)进行处理(采取行动)的类。一个 Observable 的标准流发出一个或多个 item,然后成功完成或者出错。一个 Observable 可以有多个 Subscribers,并且通过 Observable 发出的每一个 item,该 item 将会被发送到 Subscriber.onNext() 方法来进行处理。一旦 Observable 不再发出 items,它将会调用 Subscriber.onCompleted() 方法,或如果有一个出错的话 Observable 会调用 Subscriber.onError() 方法。

对于任何 Observable 你可以定义在两个不同的线程,Observable 会操作在它上面。使用 Observable.observeOn() 定义"观察者执行观察的"线程,用来监听和检查从 Observable 最新发出的 items (Subscriber 的 onNext,onCompleted 和 onError 方法会执行在 observeOn 所指定的线程上),并使用 Observable.subscribeOn() 来定义"订阅的线程",将其运行我们 Observable 的代码(长时间运行的操作)。

observeOn 与作用域

observeOn是对下游生效的,一个简单的例子:

Flowable.just(1).observeOn(Schedulers.io())
        .subscribe(i -> {
            System.out.println(Thread.currentThread().getName());
        });

输出:
RxCachedThreadScheduler-1

但是当有多个操作符,且存在多次observeOn时,每个方法都是执行在什么线程呢?

Flowable.just(1).observeOn(Schedulers.io())
        .map(i -> {
            System.out.println(Thread.currentThread().getName());
            return i;
        })
        .observeOn(Schedulers.computation())
        .subscribe(i -> {
            System.out.println(Thread.currentThread().getName());
        });

输出:
RxCachedThreadScheduler-1
RxComputationThreadPool-1

这里就涉及到一些 RxJava 实现的细节,多数操作符是基于上游调用onNext / onComplete / onError 的进一步封装,在不涉及包含Scheduler的操作符的情况下,在上游调用了observeOn后,后续操作符的方法都是执行在上游observeOn所调度的线程。因此每个操作符所执行的线程都是由上游最近的一个observeOn的Scheduler决定。

因此笔者称之为最近生效原则,但是请注意,observeOn是影响下游的,因此操作符所执行的线程受的是最近上游的observeOn影响。

示例

因此在实际使用中灵活的使用observeOn,使得代码的效率最大化。这里笔者再举个例子:

Flowable.just(new File("input.txt"))
        .map(f -> new BufferedReader(new InputStreamReader(new FileInputStream(f))))
        .observeOn(Schedulers.io())
        .flatMap(r -> Flowable.generate(() -> r, (br, e) -> {
            String s = br.readLine();
            if (s != null) {
                e.onNext(s);
            } else {
                System.out.println(Thread.currentThread().getName());
                e.onComplete();
            }
        }, BufferedReader::close))
        .observeOn(Schedulers.computation())
        .map(Integer::parseInt)
        .reduce(0, (total, item) -> {
            System.out.println(item);
            return total + item;
        })
        .subscribe(s -> {
            System.out.println("total: " + s);
            System.out.println(Thread.currentThread().getName());
        });

输出:
RxCachedThreadScheduler-1
1
2
3
4
5
total: 15
RxComputationThreadPool-1

如上代码所示,我们从 input.txt 读出每行的字符串,然后转成一个 int, 最后求和。这里我们灵活地使用了两次observeOn,在读文件时,调度至IoScheduler,随后做计算工作时调度至ComputationScheduler,从控制台的输出可以见线程的的确确是我们所期望的。当然这里求和只是一个示例,读者们可以举一反三。

事实上上面的代码还不是最优的:

Flowable.just(new File("input.txt"))
        .map(f -> new BufferedReader(new InputStreamReader(new FileInputStream(f))))
        .observeOn(Schedulers.io())
        .flatMap(r -> Flowable.generate(() -> r, (br, e) -> {
            String s = br.readLine();
            if (s != null) {
                e.onNext(s);
            } else {
                System.out.println(Thread.currentThread().getName());
                e.onComplete();
            }
        }, BufferedReader::close))
        .parallel()
        .runOn(Schedulers.computation())
        .map(Integer::parseInt)
        .reduce((i, j) -> {
            System.out.println(Thread.currentThread().getName());
            return i + j;
        })
        .subscribe(s -> {
            System.out.println("total: " + s);
            System.out.println(Thread.currentThread().getName());
        });
输出:
RxCachedThreadScheduler-1
RxComputationThreadPool-1
RxComputationThreadPool-2
RxComputationThreadPool-4
RxComputationThreadPool-4
total: 15
RxComputationThreadPool-4

如上代码所示我们可以充分利用多核的性能,通过parallel来并行运算,当然这里用在求和就有点杀鸡用牛刀的意思了,这里只是一个举例。更多 parallel 相关的内容,留待后续分享。

subscribeOn与作用域

事实上subscribeOn同样遵循最近生效原则,但是与observeOn恰恰相反。操作符会被最近的下游的subscribeOn调度,因为subscribeOn影响的是上游。

但是和observeOn又有一些微妙的差别在于,我们通常调用subscribeOn更加关注最上游的数据源的线程。因此通常不会在中间过程中调用多次,任意的调用一次subscribeOn均会影响上游所有操作符的subscribe所在的线程,且不受observeOn的影响。这是由于这两者机制的不同,subscribeOn是将整个上游的subscribe方法都调度到目标线程了。

你可能感兴趣的:(Rxjava)