RxJava的观察者类有许多方法,可以转换发出的字节流为任何你需要的数据类型。这些方法是RxJava非常核心的方法,是RxJava具有吸引力的重要缘故。

但是有些方法无论如何都不能改变流本身,我称这些方法为副作用(Side Effect)方法。

关于副作用方法,我的一点观点

副作用方法并不影响你的字节流本身。相反地,当某些事件发生时它们被调用,这样允许你去处理这些事件。

举个例子:当一些错误发生了,如果你想在你的订阅者回调函数之外做些处理,你可以使用 doOnError()方法并且将被使用的功能接口作为参数传递给方法,任何一个错误出现都可以这样做:

someObservable
.doOnError(new Action1() {
@Override
public void call(Throwable t) {
// use this callback to clean up resources,
// log the event or or report the
// problem to the user
}
})
//…

call()方法是最重要的部分。在订阅者的onError()方法被执行前这个方法的代码将被执行。

除了异常外,RxJava提供了更多可处理的事件:

事件和事件相应的副作用操作

Method

Functional Interface

Event

doOnSubscribe()

Action0

A subscriber subscribes to the Observable

doOnUnsubscribe()

Action0

A subscriber unsubscribes from the subscription

doOnNext()

Action1

The next item is emitted

doOnCompleted()

Action0

The Observable will emit no more items

doOnError()

Action1

An error occurred

doOnTerminate()

Action0

Either an error occurred or the Observable will emit no more items

doOnEach()

Action1< Notification< T>>

Either an item was emitted, the Observable completes or an error occurred. The Notification object contains information about the type of event

doOnRequest()

Action1

A downstream operator requests to emit more items


< T>要么指的是被传递进来的参数类型,要么指的是,像onError()方法这种情况里,异常可以抛出的类型

功能接口全是Action0 或者 Action1. 类型。这意味着这些接口的单一方法不返回任何值并且要么没有参数,要么只有一个参数,视特定的事件而定。

因为这些方法不返回任何值,他们不能被用来改变传递出来的数据,因此无论如何也不能改变字节流本身。相反这些方法将意图引起副作用方法,像在磁盘上写入一些东西、清空状态或者其他任何能操纵系统本身状态而不是事件流本身的事情。

注意:副作用方法本身(doOnNext(),doOnCompleted()等等)返回可观察事件,这保持了接口的流畅性。但是,这些副作用方法返回的可观察者和源观察者具有相同的类型,并且发射出同样的东西。

他们被用来在做什么?

既然他们不改变流本身,那必然有其他的用途。我在这里陈述三个例子,关于你使用这些方法可以达到的事情。

使用doOnNext()来调试
在flatMap()里使用doOnError()作为错误处理。
使用doOnNext()去保存/缓存网络结果
所以,让我们来具体看下如何使用这些例子。

使用 doOnNext()调试
使用RxJava,有时候你会疑惑为什么你的观察者不像预想中那样起作用。尤其是当你刚刚开始使用RxJava的时候。因为你用了一个流畅的API去转换一些源码为某些你想订阅的事件,你只看到了你在这个传输管道的末尾能得到的东西。

当我开始学习RxJava的时候,我有一些关于java流的使用经验。同样的,你在这里也有一些相同的问题。你有一个顺畅好用的API去转换一种类型的流为另一种类型的流。但是为什么它不像预期那样起作用呢?

在Java 8的流里,你有方法peek()可用。所以当我开始使用RxJava时,我好奇有哪些地方可以比较可以被利用。嗯,有的。其实,RxJava为我们提供了更多!

你可以在进程管道的任何地方使用doOnNext()方法,去查看发生了什么并且获得中间结果。

例子:

Observable someObservable = Observable
.from(Arrays.asList(new Integer[]{2, 3, 5, 7, 11}))
.doOnNext(System.out::println)
.filter(prime -> prime % 2 == 0)
.doOnNext(System.out::println)
.count()
.doOnNext(System.out::println)
.map(number -> String.format(“Contains %d elements”, number));
Subscription subscription = o.subscribe(
System.out::println,
System.out::println,
() -> System.out.println(“Completed!”));

这里是代码的输出:

2
3
3
5
5
7
7
11
11
4
Contains 4 elements
Completed!

当你的观察者对象表现不如你所预期时,用这种方式你可以搜集到有价值的信息,关于观察者对象是如何运行的。

doOnError()和doOnCompleted()方法在调试你管道的状态时也是有用的。

注意:如果你使用RxJava开发安卓,请看一下Frodo和Fernando Ceja​的文章,这些文章解释了使用Frodo的动机,使用Frodo可以让你通过注解的方式去调试你的观察者和订阅者。

给出了使用doOnNext()和doOnError()的方式,但并没有改变太多系统的状态——除了膨胀了你的日志和减缓了一切运行程序。

但这些操作有其他的用处,事实上,在这些用例里,你使用这些方法确实可以改变你系统的状态 ,来看一看他们吧。

在flatMap()里使用doOnError()
假设你使用Retrofit来访问网络资源。自从Retrofit支持观察者以来,在你的处理链里,你可以很容易的通过flatMap()使用这些调用。

哎,网络关联调用能引起很多方面的错误——尤其在手机上。在这个案例里,你也许不想让观察者停止工作,但如果你独自依赖于订阅者的回调方法onError(),它将停止工作。

但是请记住在方法flatMap()内有观察者。因此,你可以使用doOnError()方法通过某种方式更改UI,然而仍然有一个工作着的观察者流侦测未来可能发生的事情。

所以这个案例具体如下:

flatMap(id -> service.getPost()
.doOnError(t -> {
// report problem to UI
})
.onErrorResumeNext(Observable.empty())

如果查询你的远程资源作为经常会发生的UI事件的响应结果,这个方法尤其有用。

使用doOnNext()去保存/缓存网络结果
如果在业务链的中的某个点,你创建了一个网络调用,就可以使用doOnNext()去存储即将获得的结果到本地数据库或者将结果放入某些缓存里。

它就像接下来的这些代码行一样简单:

// getOrderById is getting a fresh order
// from the net and returns an observable of orders
// Observable<Order> getOrderById(long id) {…}
Observable.from(aListWithIds)
.flatMap(id -> getOrderById(id)
.doOnNext(order -> cacheOrder(order))
// carry on with more processing

总结

如你所看见,你可以用多种方式使用RxJava的副作用方法。即使他们没有改变事件流本身,但是它们能改变你整个系统的状态。这可能会简单得像在进程管道中的某个确定的点记录下可观察者的当前的事件,再把它们作为网络回调的结果写入数据库中。