Reactive programming,响应式编程,是一种关注异步以及事件流的编程范式。
Rx 通过观察者模式来 push 事件,避免线程阻塞的问题。所以能够很好地支持异步编程。
迭代器模式的同步 pull 与观察者模式异步 push 的比较
event | Iterable (pull) | Observable (push) |
---|---|---|
retrieve data | T next() | onNext(T) |
discover error | throws Exception | onError(Exception) |
complete | !hasNext() | onCompleted() |
Iterable:
getDataFromLocalMemory()
.skip(10)
.take(5)
.map({ s -> return s + " transformed" })
.forEach({ println "next => " + it })
Observable:
getDataFromNetwork()
.skip(10)
.take(5)
.map({ s -> return s + " transformed" })
.subscribe({ println "onNext => " + it })
通过引入函数式的操作符,可以对事件进行逐步、组合处理。函数式操作大大提高了抽象程度,避免不必要的实现细节,更容易理解。
使用 Rx 框架最简步骤可分为:
Observable.just(items) // 1
.subscribeOn(Schedulers.newThread()) // 3
.observeOn(AndroidSchedulers.mainThread()) // 3
.subscribe(observer); // 2
每个 Operator 都配了 marble diagram 进行说明,下图是对这种图的解释:
Observer 对 item 有三种响应动作:
Subscriber 是对 Observer 的特殊实现,实现了 unsubscribe 方法。此方法可以解除对 Observable 的订阅。如果 Observable 没有任何 Observer 订阅,则会停止 item 的分发。但这个停止的过程并不是实时的。
Observable 分为 hot 和 cold 两种类型。Hot 类型会在创建后立刻开始分发 item;Cold 类型会等待一个 Observer 订阅后才开始分发 item。
一般来说 cold 类型的 Observable 会是数据库查询、网络请求等。Hot 类型一般是键鼠事件、系统事件等。
在一些 Rx 实现中,还有一种 Connectable 类型,无论当前有没有 Observer订阅,在调用 connect 方法后才开始分发 item。
Rx 提供了大量的操作符,大部分函数式方法都有对应的操作符。
应该浏览一下官网对于操作符分类的简短说明。同页面中对于如何选择操作符的决策树也非常有帮助。
大多数操作符的实现都配图进行了解释,对理解操作符的工作方式非常有帮助。而且 javadoc 中也配了图, IDEA 的 Quick Document 功能可以显示出 marble diagram,是非常方便的功能。(但可能由于网络缘故加载图片很慢。)
Scheduler 在多线程编程中用于指定 Observable 和 Observer 运行的线程。ObserveOn 指定 Observer 的运行线程;SubcribeOn 指定 Observable 的运行线程。
Single 是 Observable 的变种。区别在于,Single 在它的 timeline 上只会分发一个 item。订阅了 Single 的 Observer 只有两种响应动作:onError 和 onSuccess。
Subject 既是 Observer 又是 Observable。如果在某些情况下不知道如何应用 Rx,那就考虑下使用 Subject。
AsyncSubject 会把从原 Observable 收到的最后一个 item 继续分发下去。如果原 Observable 发出的是错误 notification,那么 AsyncSubject 发出的也是 error。
当 Observer 订阅了 BehaviorSubject 后,先会收到订阅前发出的最新的一个 item(如果没有,则发出默认设置的 item),然后正常收到接下来的 item。
在创建后立刻开始发出 items,订阅的 Observer 只会收到订阅后发出的 items(,订阅前的 item 无法接收到)。
ReplaySubject 会将创建以来的所有 items 向订阅的 Observer 发送一遍,不管 Observer 是在什么时候订阅的。
Rx 适合使用的场景:
subscribeOn 操作符指定 Observable 运行的线程,observeOn 指定 Observer 的运行线程。
subscribe() 操作符的参数可以是 1~3 个 Action1,使用 lambda 来简化代码的情况下,可以取代 subscribe(observer) 方法。当 subscribe() 的参数为 Subscriber 时,可以多 Override 一个 onStart 的方法,执行顺序和线程为:onStart (observeOn) -> Observable (subscribeOn) -> onNext (observeOn) -> onComplete or onError (observeOn)
。
在 subscribe 调用后,会返回一个 Subscription 对象,需要在生命周期结束后调用其 unsubscribe 方法。如果有多个 Subscription,就使用 CompositeSubscription。
如果在某些情况下不知道如何应用 Rx,那就考虑下使用 Subject。
Rx 的流式处理便于理解代码,所以不推荐在操作符中写太复杂的代码。代码块都尽可能短,隐藏具体的细节,只要表明进行了什么样的操作就可以。
理想情况是每个操作符的参数都限制在一行,超出的部分抽成方法。这个建议是建立在方法名能够明确表明意图的前提下的。读代码的时候,最好不需要四处跳转来查看具体的实现。
链式调用的最后一个操作符内可以适当放宽这个要求。
Ractive programming 是来自于函数式编程的,所以基本都包含了函数式的操作,比如:
妥善使用自动补全以及官方文档。
从 Observer 创建 Observable。
这个操作符会将指定的时间间隔内的 items 的最后一个分发出来,并且丢弃掉前面的 items。相似的还有 throttleFirst。
指定一个时间段,如果在这个时间段内没有接收到下一个 item,就将前面接收到的 items 分为一组,然后将这个组的最后一个 item 分发出去,其余的丢弃。
指定时间段或者数量,将这些 items 收集成 List,然后将这个 List item 分发出去。
接收多个 Observable,每当从其中任意一个 Observable 接收到 item 时,会将每个 Observable 的最后一个 item 传递给指定的聚合方法,聚合方法负责处理这些 item。
AppObservable 提供了对 activity 和 fragment 的 bind 支持。但需要注意的是,这样的方法只是确保了在生命周期结束后,不会有 notification 被传递给 activity 或 fragment(不会再调用 onNext、onComplete 或 onError)。还是需要在 onDestroy 中进行 unsubscribe。
调用 bindXXX 后,Observer 会默认在主线程进行。
Observable.just(somethingBlockMainThread())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* an Observer */);
AppObservable.bindActivity(activity, somethingBlockMainThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSubscription = AppObservable.bindActivity(activity, somethingBlockMainThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
@Override
public void onPause() {
super.onPause();
mSubscription.unsubcribe();
}
public static void unsubscribeIfNotNull(Subscription subscription) {
if (subscription != null) {
subscription.unsubscribe();
}
}
public static CompositeSubscription getNewCompositeSubIfUnsubscribed(CompositeSubscription subscription) {
if (subscription == null || subscription.isUnsubscribed()) {
return new CompositeSubscription();
}
return subscription;
}
使用 timer 操作符可以设置延迟进行或者间隔重复执行。
interval 操作符可设置间隔重复执行。
配合 take 等操作符,可以组合成重复执行固定次数的任务。
WidgetObservable 提供了对 TextView、CompoundButton、AdapterView、AbsListView 的事件支持。
自定义监听的例子:
public static <T extends View> Observable<T> clicksFrom(T view) {
PublishSubject publishSubject = PublishSubject.create();
view.setOnClickListener((v) -> publishSubject.onNext(view));
return publishSubject.asObservable();
}
使用 Rx 来监听事件的好处主要体现在其他操作符的支持。
比如当文字变化时,通常的做法是添加 TextWatcher,会在每一次变化时进行一些操作(校检数据合法性等)。但在用户快速输入文字时,会引起不必要的校验操作。
这种情况下,可以使用 Debounce 操作符,在一定的时间没有输入后,才会真正触发校验操作。
适合情景:
// 无输入 400 millis 之后才显示搜索建议
bindSupportFragment(this, WidgetObservable.text(searchText))
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(searchObserver());
使用 combineLatest 操作符,每次触发 item 分发时,会将每个控件的最后一个事件传递给指定的方法。
Observable.combineLatest(
WidgetObservable.text(email),
WidgetObservable.text(password),
WidgetObservable.text(number),
(onEmailChangeEvent, onPasswordChangeEvent, onNumberChangeEvent) -> {
return emailValid(onEmailChangeEvent) &&
passValid(onPasswordChangeEvent) &&
numValid(onNumberChangeEvent);
})
.subscribe(aBoolean -> setValid(aBoolean));
Observable
.error(new RuntimeException("testing"))
.retryWhen(new RetryWithDelay(5, 1000))
.subscribe(observer);
retry 操作符会重新执行一次出错的 Observable。
retryWhen 操作符将延迟重新执行。
RxJava-Android-Samples 中提供了一种方法。
深入浅出 RxJava 四-在 Android 中使用响应式编程 最后也提到了这个问题。
部分第三方库比如 Retrofit 对 Rx 进行了支持。
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
如果需要自己实现 Operator,则实现 Observable.Operator
接口即可。
在实现后,将自己实现的操作符传入 Observable 的 lift() 方法。
在实现时,需要注意的点:
异步编程与响应式框架
FRP on Android:全面简洁地演示了 RxAndroid 相关的内容。
RxJava Koans:几个简单的编程问题,演示了 RxJava 的基本,并且展示了如何利用 TestSubscriber 来进行单元测试。
Operators:ReactiveX 官方关于 operators 的文档。
RxJava-Android-Samples:一些简单的示例。
The RxJava Android Module
草稿 - Reative Programming 基础
草稿 - Rx Framework
草稿 - Rx 应用