零.函数响应式编程概念
如果你曾经使用过java,那一定知道面向对象(OOP)的编程思想,也听说过AOP(面向切面编程)的编程思想;
1> 响应式编程(简称RP)
在计算机中,响应式编程是一种面向数据流和变化传播的编程范式,者意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
传统的编程方式是顺序执行的,需要等待直至完成上一个任务之后才会执行下一个任务,无论是提升机器性能还是代码的性能,本质上都需要依赖上一个个任务的完成,如果需要响应迅速,就得把同步执行的方式换成异步执行,方法执行变成消息发送,这就是异步编程方式,它是响应式编程的重要特性之一。
响应式编程有以下几个特点
-
异步编程:提供了合适的异步编程模型,能够挖掘多核CPU的能力,提高效率,降低延迟和阻塞等;
-
数据流: 基于数据流模型,响应式编程提供一套统一的Stream风格的数据处理接口,与java中的Stream相比,响应式编程除了支持静态数据流,还支持动态数据流,并且允许复用和同时接入多个订阅者;
-
变化传播:简单来说就是以一个数据流为输入,经过一连串操作转化为另一个数据流,然后分发给各个订阅者的过程,这就像函数式编程中的组合函数,将多个函数串联起来,把一组输入数据转化为格式迥异的输出数据。
现在的 App 会和与数据事件相关的 UI 事件进行大量的交互,使用响应式编程会显得更加得心应手。
2>函数式编程(简称FP)
随着硬件能力的不断提升,单核 CPU 计算能力几乎达到极限, CPU 己经进入了多核时代,程序员转而通过并发编程、分布式系统来应对越来越复杂的计算任务。
然而并发编程是基于内存共享,多线程编程有常见的死锁、线程饥饿、竞争条件等问题,而多线程Bug也难以重新与定位。
在函数式编程中,由于数据是不可变的immutable,因此没有并发编程的问题,是线程安全的,它将计算机运算看作数学中的函数的计算,主要特点是将计算过程分解成多个可复用的函数,并且避免了状态及变量的概念;
3>函数响应式编程(简称FRP)
使用回调存在很多问题:
回调在不阻塞任何事情的情况下,解决了 Future.get()过早阻塞的问题, 由于响应结果一旦就绪Callback就会被调用, 它们天生就是高效率的,不过, 就像使用Future一样, 对于单层的异步执行来说,回调很容易使用, 对于嵌套的异步组合,它们显得非常笨拙。
函数响应式编程:
函数响应式编程结合了函数式和响应式的优点,把函数范式里的一套思路和响应式编程合起来就是函数响应式编程。
我们知道,传统的面向对象编程是通过抽象出的对象关系来解决问题,函数式编程是通过函数(function)的组合来解决问题,响应式编程是通过函数式编程的方式来解决回调地狱(Callback Hell) 的问题。
用传统的面向对象来处理异步事件不是很直观,处理并发也十分麻烦,所以才产生了函数响应式编程。
一.Reactivex介绍
1.ReactiveX的历史
ReactiveX是Reactive Extensions(响应式扩展)的缩写,一般简写为Rx,最初是LINQ(语言集成查询)的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET,社区网站是 reactivex.io Rx操作文档 :
2.什么是ReactiveX
微软给的定义是Rx是一个使用可观察数据流进行异步编程的编程接口,ReactiveX结合了观察者模式、迭代器模式和函数式编程的精华。,使用Rx,开发者可以用Observables表示异步数据流,用LINQ操作符查询异步数据流,,用Schedulers参数化异步数据流的并发处理,Rx可以这样定义:Rx = Observables(可观察序列) + LINQ(语言集成查询) + Schedulers(调度器) /ˈʃedjuːlə(r)z/ 。
ReactiveX官网对于自身的介绍是:An API for asynchronous programming with observable streams
1. An API: 首先它是个编程接口,不同语言提供不同实现,例如Java中的RxJava;
2. For asynchronous programming: 在异步编程设计中使用,例如开启子线程处理耗时的网络请求;
3. With observale stream: 基于可观察的事件流,例如观察者模式中观察者的监听;
而ReactiveX结合了如下三部分内容:
1.观察者模式,即定义对象间一种一对多的依赖关系,当一个对象改变时,则所有依赖它的对象都会被改变;
2.Iterator模式,即迭代流式编程模式
3.函数式编程模式,即提供一系列函数样式的方法供快速开发;
3.RxJava
在RxJava中,一个实现了Observer接口的对象可以订阅(subscribe)一个Observable 类的实例。订阅者(subscriber)对Observable发射(emit)的任何数据或数据序列作出响应。这种模式简化了并发操作,因为它不需要阻塞等待Observable发射数据,而是创建了一个处于待命状态的观察者哨兵,哨兵在未来某个时刻响应Observable的通知。
二.Rx模式
1.使用观察者模式
在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
2. 观察者模式角色分析
1>抽象被观察者(Subject/Observable)
又称抽象主题:它提供一个用于保存观察者对象引用的集合 和 增加、删除观察者对象的方法 及 通知所有观察者的抽象方法;
2>具体被观察者(ConcreteSubject/ConcreteObservable)
又称具体主题,保存对具体观察者对象有用的 内部状态,当具体主题(被观察者)内部状态改变时,通知所有注册过的观察者对象;
3>抽象观察者(Observer)
为所有的具体观察者定义一个更新接口,在得到主题(被观察者)通知时更新自己, 抽象观察者可用一个抽象类或一个接口来实现;
4>具体观察者(ConcreteObserver)
具体观察者实现抽象观察者所需要的更新接口update()方法,以便在得到被观察者状态更新通知时,更新自身状态;
/**
* 1.抽象被观察者--物流系统
*/
abstract class Observable {
//抽象被观察者要求子类保持一个以所有观察者对象为元素的列表集合
protected val receiverList: MutableList<Observer> //收件人列表
init {
receiverList = ArrayList()
}
//通知订阅者更新消息, 通知收件人,物流状态更新
abstract fun notifyObserver(msg: String)
//添加一个观察者(收件人)
fun attach(observer: Observer) { //添加收件人
if (!receiverList.contains(observer)) { //去重
receiverList.add(observer)
}
}
//删除一个观察者
fun detach(observer: Observer) { //移除收件人
receiverList.remove(observer)
}
//将观察者列表清空
fun clearObserver() {
receiverList.clear()
}
/**
* @return 返回被观察对象(即此对象)的观察者总数
*/
@Synchronized
fun countObservers(): Int {
return receiverList.size
}
}
/**
* 2.具体被观察者 --快递员
*/
open class Postman : Observable() {
//记录被观察者对象是否发生变化
private var changed = false
/**
* 将被观察者已变化的状态设置为true
*/
@Synchronized
fun setChanged() {
changed = true
}
/**
* 将已变化的状态重置为false
*/
@Synchronized
fun clearChanged() {
changed = false
}
/**
* 获取被观察对象是否已变化状态
*/
@Synchronized
fun hasChanged(): Boolean {
return changed
}
override fun notifyObserver(msg: String) { //逐一通知收件人
//临时存放当前观察者的状态
var arrLocal: List<Observer>
synchronized(this) {
if (!changed) return
arrLocal = receiverList
clearChanged()
}
for (observer in arrLocal) {
observer.update(msg)
}
}
}
/**
* 3.定义抽象观察者 观察者的联系方式:如手机通知
*/
interface Observer {
//监听到被观察者状态改变时的更新方法
fun update(msg: String?)
}
/**
* 4.具体的观察者1--男收件人
*/
class Boy(private val nickName: String) : Observer{
override fun update(msg: String?) {
if (msg is String) { //男收件人的具体反映
println("$nickName, 收到了消息: $msg , 一路小跑的去取快递了")
}
}
}
/**
* 4.具体的观察者2--女收件人
*/
class Girl(private val nickName: String) : Observer{
override fun update(msg: String?) {
if (msg is String) { //女收件人的具体反映
println("$nickName, 收到了消息:$msg ,让男朋友去取快递了")
}
}
}
//观察者模式场景:邮递商品-->物流系统-->快递员--->收件人
object ObserverClient {
@JvmStatic
fun main(args: Array<String>) {
//创建具体被观察者
val postman = Postman()
//创建具体观察者
val boy = Boy("北辰")
val girl = Girl("娜美")
//订阅具体被观察者
postman.attach(boy)
postman.attach(girl)
val count: Int = postman.countObservers()
postman.setChanged()
println("changed1: ${postman.hasChanged()}")
println("count: $count")
//更新被观察者状态
postman.notifyObserver("快递到了,请下楼领取")
println("changed2: ${postman.hasChanged()}")
}
}
-
创建: Rx可以方便的创建事件流和数据流;
-
组合: Rx使用查询式的操作符组合和变换数据流;
-
监听: Rx可以订阅任何可观察的数据流并执行操作;
在RxJava 中, 被观察者、观察者、subscribe()方法三者缺一不可。只有使用了 subscribe(),被观察者才会开始发送数据.
5种观察者模式的描述,只有 Flowable 支持背压,如果有需要背压的情况,则必须使用 Flowable
2. 简化代码
-
函数式风格: 对可观察数据流使用无副作用的输入输出函数, 避免了程序里错综复杂的状态;
-
简化代码: Rx的操作通常可以将复杂的难题简化为很少的几行代码;
-
异步错误处理: 传统的try/catch没办法处理异步计算, Rx提供了合适的错误处理机制;
-
轻松使用并发: Rx的Observables (包括 Observable、Flowable、Single、Completable和Maybe )和 Schedulers 可以让开发者摆脱底层的线程同步和各种并发问题;
3. 使用RxJava的优势
Rx扩展了观察者模式用于支持数据 和 事件序列, 添加了一些操作符, 它让你可以声明式的组合这些序列, 而无需关注底层的实现:
如: 线程、同步、线程安全、并发数据结构 和 非阻塞IO。
Observable通过使用最佳的方式访问异步数据序列填补了这个间隙
阻塞模式 | 单个数据 | 多个数据 |
同步 | T getData() | Iterable<T> getData() |
异步 | Future<T> getData() | Observable<T> getData() |
Rx的Observable模型让你可以像使用集合数据一样操作异步事件流,对异步事件流使用各种简单、可组合的操作;
1> Observable可组合
对于单层的异步操作来说,Java中Future对象的处理方式是非常简单有效的,但是一旦涉及到嵌套,它们就开始变得异常繁琐和复杂,使用Future很难很好的组合带条件的异步执行流程(考虑到运行时各种潜在的问题,甚至可以说是不可能的),当然,要想实现还是可以做到的,但是非常困难,或许你可以用 Future.get() ,但这样做,异步执行的优势就完全没有了,从另一方面说,Rx的Observable一开始就是为组合异步数据流准备的。
2> Observable更灵活
Rx的Observable不仅支持处理单独的标量值(就像Future可以做的),也支持数据序列,甚至是无穷的数据流。
Observable是一个抽象概念,适用于任何场景。Observable拥有它的近亲Iterable的全部优雅与灵活.
Observable是异步的双向push,Iterable是同步的单向的pull,对比如下:
事件 | Iterable(pull) 可迭代的 | Observable(push) 可被观察的 |
获取数据 | T next() | onNext(T) |
异常处理 | throws Exception | onError(Exception) |
任务完成 | !hasNext() | onCompleted() |
3> Observable无偏见
Rx对于并发性或异步性没有任何特殊的偏好,Observable可用任何方式实现,线程池、事件循环、非阻塞IO、Actor模式、任何满足你需求的,你擅长或偏好的方式都可以,无论底层实现是阻塞还是非阻塞的,客户端代码将所有与Observable的交互都当做是异步的。
Rx提供了一系列的操作符,你可使用它们来过滤(filter)、选择(select)、变换(transform)、结合(combine)和组合(compose)多个Observable,这些操作符让执行和复合变得非常高效。
你可以把Observable当做Iterable的推送方式的等价物, 使用Iterable,消费者从生产者那拉取数据, 线程阻塞直至数据准备好,使用Observable,在数据准备好时,生产者将数据推送给消费者。数据可以同步或异步的到达,这种方式更灵活。
下面的例子展示了相似的高阶函数在Iterable 和 Observable上的应用
// 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 })
Observable类型给GOF(设计模式)的观察者模式添加了两种缺少的语义,这样就和Iterable类型中可用的操作一致了;
-
生产者可以发信号给消费者, 通知它没有更多数据可用了:
对于Iterable, 一个for循环正常完成表示没有数据了; 对于Observable, 就是调用观察者的 onCompleted 方法;
-
生产者可以发信号给消费者, 通知它遇到了一个错误
对于Iterable, 迭代过程中发生错误会抛出异常;对于Observable,就是调用观察者(Observer) 的 onError 方法;
有这两种功能, Rx就能使Observable 与 Iterable 保持一致了, 唯一的不同是数据流的方向,任何对Iterable的操作,
都可对Observable使用;
4.RxJava2的 Hello World
Rxjava版本的Hello world
Observable.just("Hello World").subscribe(s -> System.out.print(s));
5.名词定义
-
Reactive 直译为反应性的, 有活性的, 根据上下文一般翻译为反应式、响应式;
-
Iterable 可迭代对象, 支持以迭代器的形式遍历, 许多语言中都存在这个概念;
-
Observable 可观察对象 在观察者模式中是被观察的对象,一旦数据产生或发生变化, 会通过某种方式通知观察者或订阅者;
-
Observer 观察者对象, 监听Observable发射的数据并做出响应, Subscriber 是它的一个特殊实现;
-
emit 直译为发射, 发布, 发出, 含义是Observable在数据产生或变化时发送通知给Observer, 调用Observer对应的方法;
-
items 直译为项目, 条目, 在Rx里指Observable发射的数据项,文章里一律译为数据,数据项。
三. Observable
- 概述
在ReactiveX中, 一个观察者(Observer)订阅一个可观察对象(Observable)。观察者对Observable发射的数据或数据序列作出响应,这种模式可极大地简化并发操作,因为它创建了一个处于待命状态的观察者哨兵,在未来某个时刻响应Observable的通知,不需要阻塞等待Observable发射数据。
这篇文章会解释什么是响应式编程模式(Reactive pattern), 以及什么是可观察对象(Observables) 和 观察者 (observers), 其他几篇文章会展示如何用操作符组合和改变Observable的行为。
- 背景知识
在很多软件编程任务中,或多或少你都会期望你写的代码能按照编写的顺序,一次一个的顺序执行和完成。但是在ReactiveX中, 很多指令可能是并行执行的,之后他们的执行结果才会被观察者捕获,顺序是不确定的,为达到这个目的,你定义一种获取和变换数据的机制而不是调用一个方法, 在这种机制下,存在一个可观察对象(Observable), 观察者(Observer)订阅(Subscribe)它,当数据就绪时,之前定义的机制就会分发数据给一直处于等待状态的观察者哨兵。
这种方法的优点是, 如果你有大量的任务要处理, 它们互相之间没有依赖关系,你可以同时开始执行它们, 不用等待一个完成在开始下一个(用这种方式,你的整个任务队列能耗费的最长时间,不会超过任务里最耗时的那个)。
有很多术语可用于描述这种异步编程和设计模式, 在本文里我们使用这些术语: 一个观察者订阅一个可观察者对象(An observer subscribes to an Observable)。通过调用观察者的方法,Observable发射数据或通知给他的观察者。
-
RxJava 的使用
在RxJava中,被观察者、观察者、subscribe()方法三者缺一不可,只有使用了subscribe(),被观察者才会开始发送数据;
1> 创建Observable
Observable即被观察者,使用RxJava需要创建一个被观察者,它会决定什么时候触发事件以及触发怎样的事件,有点类似上游发送命令,可以在这里决定异步操作模块的顺序和异步操作模块的次数。
2>创建Observer
Observer即观察者,它可以在不同的线程中执行任务,这种模式可以极大地简化并发操作,因为它创建了一个处于待命状态的观察者哨兵,可以在未来某个时刻响应Observable的通知,而不需要阻塞等待Observable发射数据.
3>使用subscribe()进行订阅
创建了Observable和Observer之后,我们还需要使用subscribe()方法将它们连接起来,这样整个上下游就能衔接起来实现链式调用;
普通的方法调用(不是某种异步方法,也不是Rx中的并行调用),流程通常是这样的:
在异步模型中流程更像这样的:
-
定义一个方法,这个方法拿着某个异步调用的返回值做一些有用的事情,这个方法是观察者(Observer)的一部分。
-
将这个异步调用本身定义为一个Observable。
-
观察者(Observer)通过订阅(Subscribe)操作关联到那个Observable(被观察者);
-
继续你的业务逻辑,等方法返回时,Observable会发射结果,观察者的方法会开始处理结果或结果集;
Subscribe方法用于将观察者连接到Observable, 一个重载方法的版本,subscribe(onNext, onError, onComplete)
onNext(T item)
Observable 调用这个方法发射数据,方法的参数就是Observable发射的数据,这个方法可能会被调用多次,取决于你的实现。
onError(Exception ex)
当Observable遇到错误或无法返回期望的数据时会调用这个方法, 这个调用会终止Observable,后续不会再调用onNext
和 onCompleted, onError方法的参数是抛出的异常;
onCompleted()
正常终止,如果没有遇到错误,Observable在最后一次调用onNext之后调用此方法。
根据Observable协议的定义, onNext可能会被调用零次或很多次,最后会有一次onCompleted 或 onError调用(不会同时),
传递数据给onNext通常被称作发射, onCompleted 和 onError被称作通知。
用代码描述:
Observable.just("Hello World!").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
System.out.println("Next-> " + s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
System.out.println("Error-> " + throwable.getMessage());
}
}, new Action() {
@Override
public void run() throws Exception {
System.out.println("Complete.");
}
});
// 执行结果
Next-> Hello World! Complete.
}
onComplete是一个Action,它与Consumer的区别如下
-
Action无参数类型;
-
Consumer:单一参数类型;
再来看一个重载方法的版本,subscribe(onNext,onError,onComplete,OnSubscribe)
Observable.just("Hello World!").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
System.out.println("Next-> " + s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
System.out.println("Error-> " + throwable.getMessage());
}
}, new Action() {
@Override
public void run() throws Exception {
System.out.println("Complete.");
}
}, new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
System.out.println("subscribe");
}
});
// 执行结果
subscribe
Next-> Hello World!
Complete.
从打印结果可知:先执行了onSubscribe 再执行了 onNext和onComplete
在RxJava2中,Observable不再支持订阅Subscriber,而是需要使用Observer作为观察者;
Observable.just("Hello World!").subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable disposable) {
System.out.println("subscribe");
}
@Override
public void onNext(@NonNull String s) {
System.out.println("Next ->" + s);
}
@Override
public void onError(@NonNull Throwable throwable) {
System.out.println("Error->" + throwable.getMessage());
}
@Override
public void onComplete() {
System.out.println("Complete");
}
});
//执行结果
subscribe
Next ->Hello World!
Complete
2. Observables 的热和冷
Observable什么时候开始发射数据序列?这取决于Observable的实现,
热的 Observable 无论有没有观察者进行订阅,事件始终都会发射(数据可能丢失不完整),当Hot Observable有多个观察者进行订阅时,热Observable与订阅者们的关系是一对多的关系,可以与多个订阅者共享信息;
一个冷的Observable会一直等待,直到有观察者订阅它才开始发射数据,并且冷Observable 和 Observer只能是一对一关系,当有多个不同的订阅者时,消息是重新完整发送的,也就是说对Cold Observable,有多个Observer时,它们各自的时间都是独立的,因此这个观察者可以确保会收到整个数据序列。
在一些ReactiveX的视线里,还存在一种被称作Connectable的Observable,不管有没有观察者订阅它,这种Observable都不会开始发射数据,除非Connect方法被调用。
1> Cold Hot区别
-
把 Hot Observable想象成一个广播电台,所有在此刻收听的听众都会听到同一首歌;
-
Cold Observable 一张音乐CD, 人们可以独立购买并听取它;
2> Cold Observable
Observable 的just、create、range、fromxxx等操作符都能生成Cold Observable。
Consumer<Long> subscriber1 = new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
System.out.println("subscribe1->" + aLong);
}
};
Consumer<Long> subscriber2 = new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
System.out.println("subscribe2->" + aLong);
}
};
Observable<Long> observable = Observable.create(new ObservableOnSubscribe<Long>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Long> observableEmitter) throws Exception {
Observable.interval(10, TimeUnit.MILLISECONDS, Schedulers.computation())
.take(Integer.MAX_VALUE)
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
observableEmitter.onNext(aLong);
}
});
}
}).observeOn(Schedulers.newThread());
observable.subscribe(subscriber1);
observable.subscribe(subscriber2);
// 执行结果 subscriber2-> 0 subscriber1-> 0 subscriber1-> 1 subscriber2-> 1 subscriber1-> 2 subscriber2-> 2 subscriber2-> 3 subscriber1-> 3 subscriber1-> 4 subscriber2-> 4 subscriber1-> 5 subscriber2-> 5 subscriber2-> 6 subscriber1-> 6 subscriber1-> 7 subscriber2-> 7 subscriber2-> 8 subscriber1-> 8 subscriber1-> 9 subscriber2-> 9
subscriber1 和 subscriber2 结果不一定是相同的,它们二者是完全独立的,create 创建的Observable 是 Cold Observable
3. 用操作符组合Observable
对于RX来说,Observable 和 Observer 仅仅是个开始,本身不过是标准观察者模式的一些轻量级扩展,目的是更好的处理事件序列。
Rx真正强大的地方在于它的操作符,操作符让你可以变换、组合、操纵和处理Observable发射的数据。
Rx的操作符让你可用声明式的风格组合异步操作序列,它拥有回调的所有效率优势,同时又避免了典型的异步系统中嵌套回调的缺点。
下面是常用的操作符列表:
-
创建操作 Create, Defer, Empty/Never/Throw, From, Interval, Just, Range, Repeat, Start, Timer
-
变换操作 Buffer, FlatMap, GroupBy, Map, Scan和Window
-
过滤操作 Debounce, Distinct, ElementAt, Filter, First, IgnoreElements, Last, Sample, Skip, SkipLast, Take, TakeLast
-
组合操作 And/Then/When, CombineLatest, Join, Merge, StartWith, Switch, Zip
-
错误处理 Catch和Retry
-
辅助操作 Delay, Do, Materialize/Dematerialize, ObserveOn, Serialize, Subscribe, SubscribeOn, TimeInterval, Timeout, Timestamp, Using
-
条件和布尔操作 All, Amb, Contains, DefaultIfEmpty, SequenceEqual, SkipUntil, SkipWhile, TakeUntil, TakeWhile
-
算术和集合操作 Average, Concat, Count, Max, Min, Reduce, Sum
-
转换操作 To
-
连接操作 Connect, Publish, RefCount, Replay
-
反压操作,用于增加特殊的流程控制策略的操作符
这些操作符并不全都是ReactiveX的核心组成部分,有一些是语言特定的实现或可选的模块。
四.Single
1.特点
Single类似于Observable, 不同的是只发射一个数据值或一个错误通知,发送完成后就会终止订阅关系,而不是发射一系列的值。
因此,不同于Observable需要三个方法 onNext, onError, onCompleted, 订阅Single只需两个方法:
-
onSuccess - Single发射单个的值到这个方法;
-
onError - 如果无法发射需要的值, Single发射一个Throwable对象到这个方法;
Single只会调用这两个方法中的一个, 而且只会调用一次,调用了任何一个方法之后,订阅关系终止。
2.Single的使用
public static void testSingleOperator() {
Disposable success = Single.create(new SingleOnSubscribe<String>() {
@Override
public void subscribe(@NonNull SingleEmitter<String> singleEmitter) throws Exception {
if (!singleEmitter.isDisposed()) {
singleEmitter.onSuccess("success");
}
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
System.out.println("onSuccess: "+s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
System.out.println("onError==>"+ throwable.getMessage());
}
});
}
3.Single的操作符
Single也可以组合使用多种操作,一些操作符让你可以混合使用Observable 和 SIngle:
操作符 | 返回值 | 说明 |
compose | Single | 创建一个自定义的操作符 |
concat and concatWith | Observable | 连接多个Single 和 Observable发射的数据 |
create | Single | 调用观察者的create方法创建一个Single |
error | Single | 返回一个立即给订阅者发射错误通知的Single |
flatMap | Single | 返回一个Single,它发射对原Single的数据执行flatMap操作后的结果 |
flatMapObservable | Observable | 返回一个Observable,它发射对原Single的数据执行flatMap操作后的结果 |
from | Single | 将Future转换成Single |
just | Single | 返回一个发射一个指定值的Single |
map | Single | 返回一个Single,它发射对原Single的数据执行map操作后的结果 |
merge | Single | 将一个Single转换成另一个Single(它发射来自另一个Single(B)的数据) |
merge and mergeWith | Observable | 合并发射来自多个Sinle的数据 |
observeOn | Single | 指示Single在指定的调度程序上调用订阅者的方法 |
onErrorReturn | Single | 将一个发射错误通知的Single转换成一个发射定数据项的Single |
subscribeOn | Single | 指示Single在指定的调度程序上执行操作 |
timeout | Single | 它给原有的Single添加超时控制,如果超时了就发射一个错误通知 |
toSingle | Single | 将一个发射单个值的Observable转换为一个Single |
zip and zipWith | Single | 将多个Single转换为一个,后者发射的数据是对前者应用一个函数后的结果 |
总结:
-
1. Single适合执行相对独立的单次任务;
-
2. Single发送的要不是成功,要不是失败,仅有这两种情况;
-
3. Single执行完一次任务后,后续任务不再执行。
4. Single、Completable以及 Maybe概述
类型 | 描述 |
Observable<T> | 能够发射0个数据,并以成功或错误事件终止 |
Flowable<T> | 能够发射0或n个数据,并以成功或错误事件终止,支持Backpressure,可以控制数据源发射的速度 |
Single<T> | 只发射单个数据或错误事件。 |
Completable | 它从来不发射数据,只处理onComplete 和 onError事件。可以看成是Rx的Runnable。 |
Maybe<T> | 能够发射0或者1个数据,要么成功,要么失败.有点类似于Optional。 |
五.Subject
Subject可看成是一个桥梁或代理,在某些Rx实现中(如RxJava),它同时充当了Observer 和 Observable的角色, 因为它是一个Observer,它可以订阅一个或多个Observable, 又因为它是一个Observable,也可以发射新的数据。
由于一个Subject订阅一个Observable,它可以触发这个Observable开始发射数据(如果那个Observable 是 "冷"的--就是说,它等待有订阅才开始发射数据)。因此有这样的效果, Subject可以把原来的那个"冷"的Observable变成"热"的。
1. Subject的种类
针对不同的场景一共有四种类型的Subject,分别是 AsyncSubject、BehaviorSubject、ReplaySubject 和 PublishSubject
值得注意的是一定要用Subject.create()的方式创建并使用,不要用just(T)、from(T)、create(T)创建,否则会导致失效...
1) AsyncSubject
一个AsyncSubject只在原始Observable完成后, 发射来自原始Observable的最后一个值。(如果原始Observable没有发射任何值, AsyncObject也不发射任何值) 它会把最后一个值发射给任何后续的观察者.简单的说使用AsyncSubject无论输入多少参数,永远只输出最后一个参数。然而,如果原始的Observable因为发生了错误而终止,AsyncSubject将不会发射任何数据,只是简单的向前传递这个错误通知
public static void testAsyncSubject() {
AsyncSubject<String> asyncSubject = AsyncSubject.create();
asyncSubject.onNext("1");
asyncSubject.onNext("2");
asyncSubject.onNext("3");
Disposable disposable = asyncSubject.subscribe(s -> System.out.println("onNext: " + s),
throwable -> System.out.println("onError" + throwable.getMessage()),
() -> System.out.println("onComplete"));
asyncSubject.onNext("4");
asyncSubject.onNext("5");
asyncSubject.onComplete();
}
输出结果
success :5
onComplete
2) BehaviorSubject
当观察者订阅BehaviorSubject时,它开始发射原始Observable最近发射的数据(若此时还没收到任何数据,它会发射一个默认值),然后继续发射其他任何来自原始Observable的数据。然而,如果原始的Observable因发生了一个错误而终止,BehaviorSubject将不会发射任何数据,只是简单的向前传递这个错误通知。
代码示例:
private static void testBehaviorSubject() {
BehaviorSubject<String> behaviorSubject = BehaviorSubject.create();
behaviorSubject.onNext("1");
behaviorSubject.onNext("2");
behaviorSubject.onNext("3");
Disposable disposable = behaviorSubject.subscribe(s -> System.out.println("onNext: " + s),
throwable -> System.out.println("onError" + throwable.getMessage()),
() -> System.out.println("onComplete"));
behaviorSubject.onNext("4");
behaviorSubject.onNext("5");
behaviorSubject.onComplete();
}
输出结果
onNext: 3
onNext: 4
onNext: 5
onComplete
3) PublishSubject
publishSubject只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者,需要注意的是,PublishSubject可能会创建完成就立刻开始发射数据(除非你可以阻止它发生),因此这里有一个风险: 在Subject被创建后到有观察者订阅它之前这个时间段内,一个或多个数据可能会丢失,如果要确保来自原始Observable的所有数据都被分发,你需要这样做: 或者使用Create创建那个Observable以便手动给他引入"冷"Observable的行为(当所有观察者都已订阅时才开始发射数据),或者改用ReplaySubject。
如果原始的Observable因为发生了一个错误而终止,PublishSubject将不会发射任何数据,只是简单的向前传递这个错误通知。
代码示例
private static void testPublishSubject() {
PublishSubject<String> publishSubject = PublishSubject.create();
publishSubject.onNext("1");
publishSubject.onNext("2");
publishSubject.onNext("3");
Disposable disposable = publishSubject.subscribe(s -> System.out.println("onNext: " + s),
throwable -> System.out.println("onError" + throwable.getMessage()),
() -> System.out.println("onComplete"));
publishSubject.onNext("4");
publishSubject.onNext("5");
publishSubject.onComplete();
}
输出结果
onNext: 4
onNext: 5
onComplete
4) ReplaySubject
ReplaySubject会发射所有来自原始Observable的数据给观察者,无论它们是何时订阅的,也有其他版本的ReplaySubject,
在重放缓存增长到一定大小的时候或过了一段时间后会丢弃旧的数据(原始Observable发射的);
如果你把ReplaySubject当做一个观察者使用,注意不要从多线程中调用它的onNext方法(包括其他的on系列方法),
这可能导致同时(非顺序)调用,这会违反Observable协议,给Subject的结果增加了不确定性;
示例代码
private static void testReplaySubject() {
ReplaySubject<String> replaySubject = ReplaySubject.create();
replaySubject.onNext("1");
replaySubject.onNext("2");
replaySubject.onNext("3");
Disposable disposable = replaySubject.subscribe(s -> System.out.println("onNext: " + s),
throwable -> System.out.println("onError" + throwable.getMessage()),
() -> System.out.println("onComplete"));
replaySubject.onNext("4");
replaySubject.onNext("5");
replaySubject.onComplete();
}
输出结果
onNext: 1
onNext: 2
onNext: 3
onNext: 4
onNext: 5
onComplete
总的来说Subject没法指定异步线程,更像是EventBus通过订阅来实现事件通知。
2. RxJava的对应类
假设你有一个Subject,你想把它传递给其它的代理或者暴露它的Subscriber接口,你可以调用它的asObservable方法,这个方法返回一个Observable。具体使用方法可以参考Javadoc文档。
3. 串行化
如果你把 Subject当作一个 Subscriber 使用,注意不要从多个线程中调用它的onNext方法(包括其它的on系列方法),这可能导致同时(非顺序)调用,这会违反Observable协议,给Subject的结果增加了不确定性。要避免此类问题,你可以将 Subject 转换为一个 SerializedSubject ,类似于这样:
mySafeSubject = new SerializedSubject( myUnsafeSubject );
六.调度器 Scheduler
如果你想给Observable操作符链添加多线程功能,你可以指定操作符 (或者特定的Observable) 在特定的调度器(Scheduler)上执行。
某些Rx的Observable操作符有一些变体,它们可接受一个Scheduler参数。该参数指定操作符将它们的部分或全部任务放在一个特定的调度器上执行。
使用ObserveOn和SubscribeOn操作符,你可以让Observable在一个特定的调度器上执行,ObserveOn指示一个Observable在一个特定的调度器上调用观察者的onNext, onError和onCompleted方法,SubscribeOn更进一步,它指示Observable将全部的处理过程(包括发射数据和通知) 放在特定的调度器上执行。
1. 调度器的种类
下表展示了RxJava中可用的调度器种类:
调度器类型 | 效果 |
Schedulers.computation() | 用于计算任务,如事件循环或回调处理,不要用于IO操作(IO操作请使用Scheduler.io)默认线程数等于处理器的数量; |
Schedulers.from(executor) | 使用指定的Executor作为调度器 |
Schedulers.immediate() | 在当前线程立即开始执行任务 |
Schedulers.io() | 用于IO密集型任务,如异步阻塞IO操作,这个调度器的线程池会根据需要增长; 对于普通的计算任务,请使用Schedulers.computation(); schedules.io()默认是CachedThreadScheduler很像是有线程缓存的新线程调度器; |
Schedulers.newThread() | 为每个任务创建一个新线程 |
Schedulers.trampoline() | 当其他排队的任务完成后,在当前线程排队开始执行 |
AndroidSchedulers.mainThread() | 用于Android的UI更新操作 |
2. 默认调度器
在RxJava中, 某些Observable操作符的变体允许你设置用于操作执行的调度器, 其他的则不在任何特定的调度器上执行, 或者
在一个指定的默认调度器上执行, 下面的表格列出了一些操作符的默认调度器:
操作符 | 调度器 |
buffer(timespan) | computation |
buffer(timespan, count) | computation |
buffer(timespan, timeshift) | computation |
debounce(timeout, unit) | computation |
delay(delay, unit) | computation |
delaySubscription(delay, unit) | computation |
interval | computation |
repeat | trampoline |
replay(time, unit) | computation |
replay(buffersize, time, unit) | computation |
replay(selector, time, unit) | computation |
replay(selector, buffersize, time, unit) | computation |
retry | trampoline |
sample(period, unit) | computation |
skip(time, unit) | computation |
skipLast(time, unit) | computation |
take(time, unit) | computation |
takeLast(time, unit) | computation |
takeLast(count, time, unit) | computation |
takeLastBuffer(time, unit) | computation |
takeLastBuffer(count, time, unit) | computation |
throttleFirst | computation |
throttleLast | computation |
throttleWithTimeout | computation |
timeInterval | immediate |
timeout(timeoutSelector) | immediate |
timeout(firstTimeoutSelector, timeoutSelector) | immediate |
timeout(timeoutSelector, other) | immediate |
timeout(timeout, timeUnit) | computation |
timeout(firstTimeoutSelector, timeoutSelector, other) | immediate |
timeout(timeout, timeUnit, other) | computation |
timer | computation |
timestamp | immediate |
window(timespan) | computation |
window(timespan, count) | computation |
window(timespan, timeshift) | computation |
3. 使用调度器
除了将这些调度器传递给RxJava的Observable操作符,你也可用它们调度你自己的任务,下面示例展示了Scheduler.Worker的用法
worker = Schedulers.newThread().createWorker();
worker.schedule(new Action(){
@override
public void call(){
yourwork();
}
});
//some time later...
worker.unsubscribe();
4. 递归调度器
要调度递归的方法调用,你可以使用schedule, 然后再用schedule(this) 示例:
worker = Schedulers.newThread().createWorker();
worker.schedule(new Action(){
@override
public void call(){
yourwork();
//递归直到调用unsubscribe(如果调用unsubscribe调度程序将不做任何事情)
worker.schedule(this)
}
});
//some time later...
worker.unsubscribe();
5. 检查或设置取消订阅状态
Worker类的对象实现了Subscription接口,使用它的isUnsubscribed 和 unsubscribe方法,所以你可以在订阅取消时停止任务,或者从正在调度的任务内部取消订阅,示例:
Worker worker = Schedulers.newThread().createWorker();
Subscription mySubscription = worker.schedule(new Action(){
@override
public void call(){
while(!worker.isUnsubscribed()){
status = yourwork();
if(QUIT == status){ worker.unsubscribe();}
}
}
});
Worker同时是Subscription,因此你可以(通常也应该)调用它的unsubscribe方法通知可以挂起任务和释放资源了。
6.延时和周期调度器
你可使用schedule(action,delayTime,timeUnit)在指定的调度器上延迟执行你的任务, 下面例子中的任务将在500毫秒之后执行
someScheduler.schedule(someAction, 500, TimeUnit.MILLISECONDS);
使用另一个版本的schedule,schedulePeriodically(action,initialDelay,period,timeUnit)方法让你可以安排一个定期执行的任务,下面例子的任务将在500毫秒之后执行,然后每250毫秒执行一次:
someScheduler.schedulePeriodically(someAction, 500, 200,.TimeUnit.MILLISECONDS);
7.测试调度器
TestScheduler可让你对调度器的时钟表现进行手动微调,这对依赖精确时间安排的任务测试很有用处,该调度器有三个额外的方法:
-
advanceTimeTo(time,unit) 向前波动调度器的时钟到一个指定的时间点
-
advanceTimeBy(time,unit) 将调度器的时钟向前拨动一个指定的时间段
-
triggerActions( ) 开始执行任何计划中的但是未启动的任务,如果它们的计划时间等于或者早于调度器时钟的当前时间
七. Flowable
在 RxJava 2.x 中,Observable 不再支持背压(Back Pressure),而改由 Flowable 来支持非阻塞式的背压。 Flowable 是 RxJava 2.x 新增的被观察者, Flowable 可以看成 Observable 新的实现,它支持背压,同时实现 Reactive Streams 的 Publisher 接口。 Flowable 所有的操作符强制支持背压,不过 Flowable 中的操作符大多与 Observable 类似。
1.使用 Observable 较好的场景
-
一般处理最大不超过 1000 条数据,并且几乎不会出现内存溢出
-
GUI 鼠标事件,基本不会背压(可以结合 sampling/debouncing 操作)
-
处理同步流
2.使用 Flowable 较好的场景
-
处理以某种方式产生超过 lOKB 的元素;
-
文件读取与分析
-
读取数据库记录,也是一个阻塞的和基于拉取模式
-
网络 I/O 流
-
创建一个响应式非阻塞接口