使用RxJava实现反应式编程
RxJava是对Java和Android进行反应式编程的具体实现,它受到了函数式编程的影响。RxJava倡导函数组合,避免出现全局状态和副作用,并且要以流的方式思考,进而组合异步和基于事件的程序。它起源于观察者模式的生产者和消费者回调,并且扩展了几十个操作符来实现组合、转换、调度、节流、错误处理和生命周期管理。
反应式编程和RxJava
反应式编程是一个通用的编程术语,它主要关注对变更做出反应,比如数据值或事件。反应式编程通常可以按照命令式的方式实现。回调就是以一种命令式实现反应式编程的方法,电子表格就是反应式编程的一个绝佳例子:某些单元格依赖其他的单元格,如果被依赖的单元格发生变化,这些单元格也会随之“做出反应”。
反应式编程是对命令式系统之上的一种抽象,它允许开发人员编写异步和事件驱动的用例时不必像计算机本身那样思考,也不用以命令式的方式来定义复杂的状态交互,尤其是跨线程和网络边界时,在处理异步和事件驱动的系统时,不用想计算机那样思考是一项有用的性质,因为这种方式会设计并发和并行。而要正确地使用这些功能是很有挑战性的。
何时需要反应式编程
- 处理用户事件,比如鼠标移动和单击、键盘输入、GPS信号因用户设备的移动而不断发生变化、设计陀螺仪信号和触摸事件等。
- 响应和处理来自磁盘和网络的所有延迟受限的IO时间、IO本质上是异步的(发起请求、时间推移,可能受到也可能收不到响应,触摸下一步事件)
- 在应用程序中处理由该应用程序无法控制的生产者推送过来的事件或数据(来自服务器的系统事件、上述用户事件、来自硬件的信号、模拟世界中由传感器触发的事件等)
RxJava是如何运行的
RxJava的核心是Observable类型,它代表了数据或事件的流。他的目的是实现推送,但也可以是拉取。它是延迟执行的,而不是立即执行的。它可以同步使用,也可以异步使用。它能够代表随时间推移产生的0个、1个、多个或者无穷多个值或事件。
推送与拉取
RxJava实现反应式的要点在于它支持推送。
为了支持接收推送来的事件,Observable/Oberver通过订阅进行连接。Observable代表了数据流,它可以被Observer订阅。
订阅之后,Observer就能接收三种推送给它的事件
- 通过onNext函数推送的数据
- 通过onError函数推送的错误
- 通过onComplete函数推送的流完成信息
异步与同步
一般而言,Obervable是异步的,但它并非总是如此,Observable可以是同步的,事实上,它默认就是同步的,除非要求,否则RxJava永远不会添加并发功能。例如,下面代码完全是同步的。
Observable.create(s -> {
s.onNext("hello world!");
s.onComplete();
}).subscribe(hello -> System.out.println(hello));
将同步阻塞IO与Observable组合使用是一种很糟糕的形式,实际上,重要的标准是Observable生成事件的过程是阻塞的还是非阻塞的,而非它是同步的还是异步的。
- 内存数据
如果数据在本地缓存中,那么再花费调度成本将其异步化就没有意义了。Observable可以同步获取数据,并将其发布到订阅线程上,如下所示:
Observable.create(s ->
s.onNext(cache.get(SOME_key));
s.onCompleted();
}).subsribe(value -> System.out.prinln(value));
不清楚数据是否在内存中的时候,调度选择是很重要的,如果数据在内存中,就采用同步的方式进行发布,如果不在内存中,就执行异步的网络调用,并在数据到达的时候将其返回。这种选择可以放到一个条件化的Observable中。
Observable.create(s -> {
T fromCache = getFromCache(SOME_KEY);
if (fromCache != null) {
// 同步发布
s.onNext(fromCache);
s.onCompleted();
} else {
// 异步抓取
getDataAsync(SOME_KEY)
.onResponse(v -> {
putInCache(SOME_KEY, v);
s.onNext(v);
s.onCompleted();
})
.onFailure(e -> {
s.onError(e);