RxJava概述
- 一、函数响应式编程
-
- [1.1、响应式编程(Reactive Programming)](#1.1、响应式编程(Reactive Programming))
- [1.2、函数式编程(Functional Programming)](#1.2、函数式编程(Functional Programming))
- [1.3、函数响应式编程(Functional Reactive Programming)](#1.3、函数响应式编程(Functional Reactive Programming))
- 二、RxJava简介
- 三、为何选择RxJava
- 四、简单示例
一、函数响应式编程
1.1、响应式编程(Reactive Programming)
在计算机中,响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进
行传播。
传统的编程方式是顺序执行的,需要等待直至完成上一个任务之后才会执行下一个任务。无论是提升机器的性能还是代码的性能,本质上都需要依赖上一个任务的完成。如果需要响应迅速,就得把同步执行的方式换成异步执行,方法执行变成消息发送。这就是异步编程的方式,它是响应式编程的重要特性之一。
响应式编程有以下几个特点。
- 异步编程:提供了合适的异步编程模型,能够挖掘多核CPU的能力、提高效率、降低延迟和阻塞等。
- 数据流:基于数据流模型,响应式编程提供一套统一的Stream风格的数据处理接口。与Java8中的Stream相比,响应式编程除了支持静态数据流,还支持动态数据流,并且允许复用和同时接入多个订阅者。
- 变化传播 :简单来说就是以一个数据流为输入,经过一连串操作转化为另一个数据流,然后分发给各个订阅者的过程。这就有点像函数式编程中的组合函数,将多个函数串
联起来,把一组输入数据转化为格式迥异的输出数据。
响应式编程一方面在用户界面编程领域及基于实时系统的动画方面都有广泛的应用;另一方面,在处理嵌套回调的异步事件、复杂的列表过滤和变换的时候也都有良好的表现。
现在的App无论是原生H5、HTML5还是Hybird,都会和与数据事件相关的UI事件进行大量的交互,使用响应式编程会显得更加得心应手。
这些年来前端比较流行的响应式设计,实际上是指网页能够自动调整布局和样式以适配不同尺寸的屏幕,与我们谈论的响应式编程是两个完全不同的概念。
1.2、函数式编程(Functional Programming)
随着硬件能力的不断提升,单核CPU的计算能力几乎达到极限,CPU已经进入了多核时代,程序员转而通过并发编程、分布式系统来应对越来越复杂的计算任务。
然而并发编程并不是银弹,作为一种基于共享内存的并发编程,多线程编程有常见的死锁、线程饥饿、竞争条件等问题,而且多线程的Bug也难以重现和定位。于是,函数式编程开始兴起。
在函数式编程中,由于数据是不可变的(immutable),因此没有并发编程的问题,是线程安全的。它将计算机运算看作数学中函数的计算,主要特点是将计算过程分解成多个可复用的函数,并且避免了状态及变量的概念。函数式编程虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程具有以下特点。
- 函数是"第一等公民":所谓"第一等公民"(First Class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
- 闭包和高阶函数:闭包是起函数的作用并可以像对象一样操作的对象。与此类似,FP语言支持高阶函数。高阶函数可以用另一个函数(间接地,用一个表达式)作为输入参数,在某些情况下,它甚至返回一个函数作为输出参数。这两种结构结合在一起,即可以用优雅的方式进行模块化编程,这是使用FP的最大好处。
- 递归:把递归作为控制流程的机制。例如,在Hask®ll的世界中,没有变量赋值和流程跳转,如果要实现一些简单的功能,比如求一个数组中的最大值,则需要借助递归来实现。
- 惰性求值(Lazy Evaluation):它表示为"延迟求值"和"最小化求值"。惰性求值使得代码具备了巨大的优化潜能。支持惰性求值的编译器会像数学家看待代数表达式那样看待函数式编程的程序,即抵消相同项从而避免执行无谓的代码,安排代码执行顺序从而实现更高的执行效率甚至是减少错误。惰性求值另一个重要的好处是它可以构造一个无限的数据类型,无须担心由无穷计算所导致的内存溢出错误。
- 没有"副作用"(Side Effect):指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
1.3、函数响应式编程(Functional Reactive Programming)
函数响应式编程结合了函数式和响应式的优点,把函数范式里的一套思路和响应式编程合起来就是函数响应式编程。
我们知道,传统的面向对象编程是通过抽象出的对象关系来解决问题,函数式编程是通过函数(function)的组合来解决问题,响应式编程是通过函数式编程的方式来解决回调地狱(Callback Hell)的问题。
用传统的面向对象来处理异步事件不是很直观,处理并发也十分麻烦,所以才产生了函数响应式编程。
二、RxJava简介
2.1、RxJava产生的由来
RxJava是Reactive Extensions的Java实现,用于通过使用Observable/.Flowable序列来构建异步和基于事件的程序的库。
RxJava扩展观察者模式以支持数据/事件序列,并添加允许你以声明方式组合序列的操作符,同时提取对低优先级的线程、同步、线程安全性和并发数据结构等问题的隐藏。
2.2、什么是Rx
ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LNQ的一个扩展,由微软架构师Erik Meijer领导的团队所开发,于2012年11月开源。Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便地处理异步数据流。Rx库支持NET、JavaScript和C++。由于Rx近几年越来越流行,因此现在已经支持几乎全部的流行编程语言。Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava、RxJS和Rx.NET,社区网站是http://reactivex.io/。
2.3、ReactiveX的历史
微软给的定义是,Rx是一个函数库,让开发者可以利用可观察序列和LNQ风格查询操作符来编写异步和基于事件的程序,通过使用Rx,开发者可以用Observables表示异步数据流,用LNQ操作符查询异步数据流,用Schedulers参数化异步数据流的并发处理。可以这样定义Rx:Rx=Observables LINQ+Schedulers.
注:在RxJava2.x中,Observables包含了Observables、Flowable、Single、Maybe、Completable等。
ReactiveX.io给的定义是,Rx是一个使用可观察数据流进行异步编程的编程接口,ReactiveX结合了观察者模式、迭代器模式和函数式编程的精华。
2.4、Rx模式
使用观察者模式。
- 创建:Rx可以方便地创建事件流和数据流。
- 组合:Rx使用查询式的操作符组合和变换数据流。
- 监听:Rx可以订阅任何可观察的数据流并执行操作。
简化代码。
- 函数式风格:对可观察的数据流使用无副作用的输入输出函数,避免了程序里错综复杂的状态。
- 简化代码:x的操作符通常可以将复杂的难题简化为很少的几行代码。
- 异步错误处理:传统的try/catch没办法处理异步计算,Rx提供了合适的错误处理机制。
- 轻松使用并发:Rx的Observables(包括Observable、Flowable、Single、Completable和Maybe)和Schedulers可以让开发者摆脱底层的线程同步和各种并发问题。
总而言之,RxJava是Reactive Extensions在JVM平台上的一个实现,通过使用观察者序列来构建异步、基于事件的程序。RxJava可以说是观察者设计模式的一个扩展,支持不同的数据事件流和额外的操作类,允许通过声明式的方式构建不同的执行序列,通过抽象的方式屏蔽底层的多线程实现、同步、线程安全、并发数据结构、非阻塞I/O等逻辑。RxJava支持Java5
之后的版本,还支持跑在JVM上的各种其他语言,例如Groovy、Clojure、JRuby、Kotlin和Scala等。笔者建议尽量使用Java8及以上的版本,因为能够使用Lambda表达式等新特性。
RxJava使用Observables来访问多对象的异步序列。Observables是可组合的,Java Futures模型在用于单一层面的异步执行方面比较方便,但是在需要异步嵌套执行时使用起来却很复杂,至少在Java8出现之前是这样的。用Futures在构建有条件的异步执行流时会非常困难,因为在运行时每次请求的延迟都是不可控的。虽然理论上能够做到,但是过程会很复杂,有时候会在Future.getO上一直阻塞,这样的结果显然无法体现异步执行的优势。在Java8之后新增了CompletableFuture,弥补了原先Futures模型的问题。
当然,RxJava不并限于是在Java8之后,还是之前使用。Observables本身是专门用于构建异步数据流和执行片段的,它很灵活,RxJava的Observables除了支持Futures模型的所有功能外,还支持无限的数据流;Observables本身是一个统一抽象,用于支持不用情况下的异步数据序列的执行过程。
三、为何选择RxJava
x扩展了观察者模式用于支持数据和事件序列,添加了一些操作符,让你可以使用声明式的方式来组合这些序列,而无须关注底层的实现,如线程、同步、线程安全、并发数据结构和
非阻塞I/O。
Rx的Observable模型让开发者可以像使用集合数据一样操作异步事件流,对异步事件流使用各种简单、可组合的操作。
- 可组合: 对于单层的异步操作来说,Java中Future对象的处理方式非常简单有效,但是一旦涉及嵌套,它们就开始变得异常烦琐和复杂。在Java8之前,使用Future很难很好地组合带条件的异步执行流程。从另一方面来说,RxJava的被观察者们(Observable/Flowable/Single/Completable/Maybe)一开始就是为组合异步数据流准备的。
- 更灵活: RxJava的Observable不仅支持处理单独的标量值(就像Future可以做的),还支持数据序列,甚至是无穷的数据流。Observable是一个抽象概念,适用于任何场景。Observable拥有它的近亲Iterable的全部优雅与灵活。
- 无偏见: Rx对于并发性或异步性没有任何特殊的偏好,Observable可以用任何方式(如线程池、事件循环、非阻塞I/O或Actor模式)来满足我们的需求。无论我们选择怎样实现它,无论底层实现是阻塞的还是非阻塞的,客户端代码都将与Observable的全部交互当成是异步的。
3.1、Observable是如何实现的
public Observable<T>getDataO);
- 它能与调用者在同一线程同步执行吗?
- 它能异步地在单独的线程执行吗?
- 它会将工作分发到多个线程,返回数据的顺序是任意的吗?
- 它使用Actor模式而不是线程池吗?
- 它使用NIO和事件循环执行异步网络访问吗?
- 它使用事件循环将工作线程从回调线程分离出来吗?
- 从Observer的视角来看,这些都无所谓,重要的是:使用Rx,可以改变我们的观念,可以在完全不影响Observable程序库使用者的情况下,彻底地改变Observable的底层实现。
3.2、使用回调存在很多问题
回调在不阻塞任何事情的情况下,解决了Future...getO过早阻塞的问题。响应结果一旦就绪,Callback就会被调用,它们天生就是高效率的。不过,就像使用Future一样,对于单层的异步执行来说,回调很容易使用,对于嵌套的异步组合而言,它们显得非常笨拙。
3.3、Rx是一个多语言的实现
Rx在大量的编程语言中都有实现,并尊重实现语言的风格,而且更多的实现正在飞速增加。
3.4、响应式编程
Rx提供了一系列的操作符,我们可以使用它们来过滤(filter)、选择(select)、变换(transform)、结合(combine)和组合(compose)多个Observable,这些操作符让执行和复合变得非常高效。
我们可以把Observable当作Iterable推送方式的等价物,使用Iterable,消费者从生产者那拉取数据,线程阻塞直至数据准备好。使用Observable,在数据准备好时,生产者将数据推送
给消费者。数据可以同步或异步到达,这种方式更加灵活。
四、简单示例

java
public class Demo {
public static void main(String[] args) {
Observable.create(new ObservableOnSubscribe<String>() {
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("Hello World");
}
}).subscribe(new Consumer<String>() {
public void accept(String s) throws Exception {
System.out.println(s);
}
});
}
}
简化版:
java
Observable.just("Hello World").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
System.out.println(s);
}
});
使用Lambda表达式:
java
Observable.just("Hello World").subscribe(System.out::println);