在第一章中,我们了解了RxJava的基本框架,第二章中我们知道操作符可以变得如何强大。但也许你可能仍然有些迟疑,要让你信服还远远不够。那么本章将会介绍一些有关RxJava的其他优点。
Error Handling
在此之前我们已经大大忽略了onComplete()和onError()。它们在Observable停止释放数据时被调用,无论是成功完成还是失败出错。而Subscriber可以监听到这两个方法:
Observable.just("Hello, world!") .map(s -> potentialException(s)) .map(s -> anotherPotentialException(s)) .subscribe(new Subscriber<String>() { @Override public void onNext(String s) { System.out.println(s); } @Override public void onCompleted() { System.out.println("Completed!"); } @Override public void onError(Throwable e) { System.out.println("Ouch!"); } });
假设potentialException()和anotherPotentialException()都由可能抛出异常,而Observable将会在onComplete()或onError()结束释放。那么这一段代码要么会输出Completed,要么会输出Ouch。这里有一些需要注意的地方:
1 在整个数据流的任何地方一旦抛出异常,onError()方法就会被调用。这会令异常处理变得轻松多了,我完全可以在每个方法后面添加异常处理方法。
2 操作符可以不用处理异常。你可以将异常交给Subscriber,让它决定如何处理。
3 你明确知道Subscriber会在何时结束接收数据项。
了解这些会使错误处理比以前更加轻松,因为如果用回调你不得不在每个回调里都加上错误处理方法,这不仅仅会带来重复代码也意味着每个回调必须明确怎样处理错误,也就表示你的回调代码必须紧紧依附于调用者。
用RxJava,你的Observable甚至都不用知道遇到异常错误该如何处理。你的其他任何操作符都不用处理错误,他们会在出现严重错误时直接跳过。你可以把你所有的错误处理都交给Subscriber。
如果你的Android应用有网络请求的话,那么你不得不将这个耗时的操作放在其他线程上,但在Android上多线程处理是很困难的,因为你必须保证在正确的线程上运行正确的代码,一旦有误程序就会崩溃。一个经典的异常就会在你尝试修改主线程上的View的时候抛出来。
使用RxJava,subscribeOn()用来决定你的observer代码在哪个线程运行,observeOn()用来决定你的Subscriber会在哪个线程运行(下面的代码为例,意思是根据url请求图片会在其他线程执行,而bitmap获取下来之后会在主线程上执行设置图片,简而言之,请求操作在subscribeOn内定义在哪条线程执行,当结果来了之后会根据observeOn内的设置来决定subscribe内的操作会在哪条线程上执行)。
myObservableServices.retrieveImage(url) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
这里需要注意的地方是subscribeOn内的设置,可以设置很多种线程,但常用的有三种,源码如下:
Subscriptions
有一件事情我一直没有提到,当调用Observable.subscribe()它会返回一个Subscription。它代表了你的Observable和subscriber的连接:
Subscription subscription = Observable.just("Hello, World!") .subscribe(s -> System.out.println(s));
换句话说,你可以在适当的时候得到这个连接进行绑定或解绑:
subscription.unsubscribe(); System.out.println("Unsubscribed=" + subscription.isUnsubscribed()); // Outputs "Unsubscribed=true"
关于RxJava很好的一个地方就是解绑意味着停止整条链,无论什么时候正在做什么,一旦解绑就会立即终止,仅此而已。这里需要解释的是,这个数据链的理解,你可以把整个数据链从开头到结尾完全连接起来去思考,有入口也有出口,数据从某个操作符进入后,中间经过了多个操作符的处理方法最终返回出口,接下来就分别发送给订阅者,即subscribe()的内容。一般的操作符都会返回Observable<T>类型,而这里的Subscription是最终出口subscribe的返回类型,因此一旦拿到它便可以随时随地进行进行数据链的控制。
Keep in mind that these articles are an introduction to RxJava. There's a lot more to learn than what I presented and it's not all sunshine and daisies (for example, read up on backpressure). Nor would I use reactive code for everything - I reserve it for the more complex parts of the code that I want to break into simpler logic.
Originally, I had planned for this post to be the conclusion of the series, but a common request has been for some practical RxJava examples in Android, so you can now continue onwards to part 4. I hope that this introduction is enough to get you started on a fun framework. If you want to learn more, I suggest reading the official RxJava wiki. And remember: the infinite is possible.
Many thanks to all the people who took the time to proofread these articles: Matthias Käppler, Matthew Wear, Ulysses Popple, Hamid Palo and Joel Drotos (worth the click for the beard alone).
1 This is one reason why I try to keep my Subscriber
as lightweight as possible; I want to minimize how much I block the main thread.
2 Deferring calls to observeOn()
and subscribeOn()
is good practice because it gives the Subscriber
more flexibility to handle processing as it wants. For instance, an Observable
might take a while, but if the Subscriber
is already in an I/O thread you wouldn't need to observe it on a new thread.
3 In part 1 I noted that Observable.just()
is a little more complex than just calling onNext()
and onComplete()
. The reason is subscriptions; it actually checks if the Subscriber
is still subscribed before calling onNext()
.