还记得一年前从Java切换到node,当初总限于Java开发的固有思维而对node的理念感到费解,踩过无数坑后,渐渐发现利用非阻塞 I/O、异步事件及函数式编程能优雅地处理并发问题,而不仅仅是new个thread。作为一名合格的开发人员应该不断地学习新的技术,深入理解背后的原理并且根据实际工作需求选用适当的技术。最近在github上发现RxJava也可以让Java开发者方便地进行响应式编程,于是花时间学习了一番,搜集整理了一些知识点,与大家分享。
Rx即Reactive Extension, 是一个集观察者模式、迭代器模式和函数式编程的响应式编程模型,为开发者处理异步数据流提供了一致的编程接口。 Rx库最初由微软开发,目前由ReactiveX社区维护(http://reactivex.io/), 已支持几乎所有的流行编程语言。
RxJava则是Rx库Java平台的扩展,对可观察的对象 (Observable)进行了抽象并提供了相关的高阶函数。
在使用Rx库之前我们有必要对响应式编程的基本概念及理念进行一番了解。
对于多数Java开发者, 异步编程是个难点,也很容易出错,大家首先会想到多线程、Future,对于单层异步操作Future显得简单有效(Future.get()也是阻塞的), 但如果涉及到多个异步操作的组合流程、嵌套,就会变得异常繁琐,稍不注意就会导致回调”地狱”(callbackhell)。 Rx的Observable为组合异步执行流程提供了方便。
Rx扩展了观察者模式用于支持数据和事件序列,添加了一些操作符,它让你可以声明式的组合这些序列,而无需关注底层的实现:如线程、同步、线程安全、并发数据结构和非阻塞IO。
从结构上来说,Rx主要组成部分为Observable、Operator、Subscriber(Observer)、Subscription、Scheduler。
- Observable即可观察对象,产生异步数据流或事件序列, Observable是一个抽象的概念,不仅支持处理单独的标量值(就像Future可以做的),也支持数据序列,甚至是无穷的数据流. 例如,一个由微博订阅事件产生的数据流,开发者监听该数据流并在此基础上可以实现粉丝增加、订阅数统计、获取订阅内容等一系列的数据处理。数据流可以看作一个按时间排序的时间序列,一般的数据流模型包括三种事件类型:普通事件(值事件)、错误事件、完成事件。Observable的操作更类似于Iterable,并具有Iterable全部灵活性。可以把Observable当做Iterable的推送方式的等价物,使用Iterable,消费者从生产者那拉取(pull)数据,线程阻塞直至数据准备好。使用Observable,在数据准备好时,生产者将数据推(push)送给消费者。数据可以同步或异步的到达,这种方式更灵活。Observable是异步的双向push,Iterable是同步的单向pull,对比如下:
事件 Iterable(pull) Observable(push) 获取数据 T next() onNext(T) 异常处理 throwsException onError(Exception) 任务完成 !hasNext onCompleted()
Observable类型给GOF的观察者模式添加了两种缺少的语义,这样就和Iterable类型中可用的操作一致了,熟悉node开发者可以联想到Promise:
1. 生产者可以发信号给消费者,通知它没有更多数据可用了(对于Iterable,一个for循环正常完成表示没有数据了;对于Observable,就是调用观察者的onCompleted方法)
2. 生产者可以发信号给消费者,通知它遇到了一个错误(对于Iterable,迭代过程中发生错误会抛出异常;对于Observable,就是调用观察者(Observer)的onError方法)
- Operator 即操作符,你可以使用它们来过滤(filter)、选择(select)、变换(transform)、结合(combine)和组合(compose)多个Observable数据流,这些操作符让执行和复合变得非常高效
- Subscription 即订阅,Subscriber和Observable通过订阅关联起来
- Subscriber则消费异步数据流或事件序列的消费者,也可以说Rx是异步、事件驱动的,观察者订阅该数据流之后,当普通事件发生时调用观察者处理普通事件的相关函数,如表单提交数据写入数据库等;当数据流发生任何错误时会抛出异常,该异常由观察者捕获并在错误处理函数中处理,这类似于try/catch方法;当整个数据流完成时,观察者会执行其数据流完成函数,如提供一些关闭窗口、断开数据库连接等功能。当然,有时候开发者可以忽略错误事件和完成事件而只需聚焦于如何定义和设计在发出值事件时要执行的函数。
- Scheduler 控制异步数据流的并发处理
初步理解上述概念后,让我们结合具体例子逐步深入:
例一: 最简单的Rx数据流模式:
Observable -> Subscriber
//java 7
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { System.out.println("Current Thread: " + Thread.currentThread().getId()); subscriber.onNext("Hello RxJava"); subscriber.onCompleted(); } }); Subscriber<String> subscriber = new Subscriber<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(String s) { System.out.println("Current Thread: " + Thread.currentThread().getId()); System.out.println(s); } }; observable.subscribe(subscriber);
此例中,数据流直接从Observable到Subscriber,没有Operator进行修改数据流的修改.
1. Subscriber对象调用obserable对象的subcriber方法和observable对象关联起来
2. 被观察者调用onNext()产生数据流”Hello World” 然后complete.
3. Observable对象调用subscriber对象的onNext()输出数据, 然后调用其onComplete()方法终止, 如果发生异常会调用subscriber的onError(Throwablee)
与经典的观察者模式相比,他们最重要的区别之一在于在没有subscriber之前,observable不会产生事件。 运行代码,从输出可见Observable和Subscriber运行在同一线程中。
Observable和操作符不需要处理错误状态-一旦发生错误,就会跳过当前和后续的操作符。所有的错误处理都交给订阅者来做。
在Java 8中可以用lambda简化为, 插入代码:
// Use Java 8 lambda instead Observable.just("Hello RxJava") .subscribe(s -> System.out.println(s));
例二:加入Operator,则Rx数据流如下:
Observable-> Operator -> … -> Operator-> Subscriber
// run in java 8
Observable.just(1, 2, 3, 4, 5) .filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer item) { return (item < 4); } }) .map(s -> s * s) .subscribe(new Subscriber<Integer>() { @Override public void onNext(Integer item) { System.out.println("Next: " + item); } @Override public void onError(Throwable error) { System.err.println("Error: " + error.getMessage()); } @Override public void onCompleted() { System.out.println("Sequence complete."); } });
上述例子调用了filter操作符过滤出小于4的整数,然后调用map操作符得到一个新的序列其中每项为之前序列中对应元素的平方,最后通过subscriber输出。 可见Rx操作符给数据流处理提供了极大方便。
例三:Scheduler多线程
Observable.just("www.sina.com","www.baidu.com") .flatMap(url -> getTitle(url)) .subscribeOn(Schedulers.newThread()).subscribe(title -> { System.out.println("Subscriber on thread: " + Thread.currentThread().getName()); System.out.println(title != null ? title : "title not found"); }); try { Thread.sleep(2000); //sleep is required or subscriber may not receive the data } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); }
此例通过subscribeOn()将网络IO操作隔离到独立的线程中执行使用observeOn和subscribeOn操作符,可以让Observable、操作符和Subscriber在特定的调度器上执行,observeOn指示一个 Observable在一个特定的调度器上调用观察者的onNext, onError和onCompleted方法,subscribeOn更进一步,它指示Observable将全部的处理过程(包括发射数据和通知)放在特定的调度器上执行。
至此我们已对Rx编程模型及RxJava库有了初步的认识,在后续系列中我们将结合更多的实例深入理解Rx的每个组件及原理)。 要高效使用Rx,还需仔细研读官方文档及原代码,并在大量实践中体会。
下面是关于Rx的一些学习资源:
示例代码: [下载]