4. RxJava操作符与应用场景

欢迎大家加入QQ群一起讨论: 489873144(android格调小窝)
我的github地址:https://github.com/jeasonlzy

前言

这篇文章可能是有史以来最详细的操作符讲解了,包括操作符的含义,代码实现,使用场景都有很详细的说明,目前涵盖了全部操作符的80%以上。

更多希望看更多详细的操作符讲解,请参考官方文档:Operators By Category

为了方便看懂下面的图形分别表示的意思,这里先来个官方图解,我就不翻译了,很简单,实在不懂可以自行百度翻译:

一、 创建类

1.1 create

方法是RxJava最基本的创造事件序列的方法。它需要一个OnSubscribe对象,这个对象继承Action1,当观察者订阅我们的Observable时,它作为一个参数传入并执行call()函数。

1.2 empty / never / throw

empty: 当我们需要一个Observable毫无理由的不再发射数据正常结束时,我们可以使用empty()。
never: 创建一个不发射数据并且也永远不会结束的Observable。
throw: 创建一个不发射数据并且以错误结束的Observable。
可能测试的时候很有用。

1.3 from

将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。from()创建符可以从一个列表/数组来创建Observable,并一个接一个的从列表/数组中发射出来每一个对象,或者也可以从JavaFuture类来创建Observable,并发射Future对象的get()方法返回的结果值。传入Future作为参数时,我们可以指定一个超时的值。Observable将等待来自Future的结果,如果在超时之前仍然没有结果返回,Observable将会触发onError()方法通知观察者有错误发生了。

1.4 just

可以传入一到九个参数,它们会按照传入的参数的顺序来发射它们。也可以接受列表或数组,就像from()方法,但是它不会迭代列表发射每个值,它将会发射整个列表。通常,当我们想发射一组已经定义好的值时会用到它。最后注意just()创建符,它发射出值后,Observable正常结束,发射onCompleted事件

1.5 range

创建一个发出一系列连续整数的Observable,它接受两个参数,一个是范围的起始值,一个是范围的数据的数目。如果你将第二个参数设为0,将导致Observable不发射任何数据(如果设置为负数,会抛异常)。

1.6 repeat / repeatWhen

repeat对Observable中的数据重复发射,它不是创建一个Observable,而是重复发射原始Observable的数据序列,这个序列或者是无限的,或者通过repeat(n)指定重复次数。

repeatWhen它不是缓存和重放原始Observable的数据序列,而是有条件的重新订阅和发射原来的Observable。将原始Observable的终止通知(完成或错误)当做一个void数据传递给一个通知处理器,它以此来决定是否要重新订阅和发射原来的Observable。这个通知处理器就像一个Observable操作符,接受一个发射void通知的Observable为输入,返回一个发射void数据(意思是,重新订阅和发射原始Observable)或者直接终止(意思是,使用repeatWhen终止发射数据)的Observable。

1.7 defer

defer为每一个observer创建一个ObservableSource,这样当第一个observer订阅之后如果ObservableSource中的数据发生变化,第二个订阅的Observer会得到不同的数据。有这样一个场景,你想在这声明一个Observable但是你又想推迟这个Observable的创建直到观察者订阅时,就可以使用这个。更多的,我们把运行缓慢的代码使用defer包一层,那么代码将非一般丝滑。



对于上图,如果observable不使用defer包裹,那么最后得到的结果是one,但是使用defer以后,即使name的值在observable创建之后改变,但是只要在subcribe之前,那么最后得到的数据也是最新的数据,即two

1.8 interval

interval可以延时一定时间后开始按周期emit数据,emit的数据从0开始一次递增。当有“每隔xx秒后执行yy操作”类似的需求的时候,想到使用interval


第一个参数为第一次emit数据时延时时间
第二个参数为emit数据周期
第三个参数为时间单位
由于周期性不停止的emit数据,所以需要在界面销毁的时候,停止订阅和发送,否则会发生内存泄露。

1.9 timer

延时一定时间emit 数据0,当有“x秒后执行y操作”类似的需求的时候,想到使用timer。


上图会在两秒后输出结果:0

二、 过滤类

2.1 filter

filter按照一定的规则过滤数据


上图最后结果:2,4,6

2.2 take / takeFirst / takeLast / takeUntil / takeWhile

  • take / takeFirst只取前几个数据
  • takeLast只取最后几个数据
  • takeUntil订阅并开始发射原始Observable,它还监视你提供的第二个Observable。如果第二个Observable发射了一项数据或者发射了一个终止通知,TakeUntil返回的Observable会停止发射原始Observable并终止。
  • takeWhile发射原始Observable,直到你指定的某个条件不成立的那一刻,它停止发射原始Observable,并终止自己的Observable。



图中只取前三个数据,最后结果:1,2,3

2.3 skip / skipLast / skipUntil / skipWhile

  • skip会跳过前几个数据
  • skipLast会跳过最后几个数据,这个机制是这样实现的:延迟原始Observable发射的任何数据项,直到它发射了N项数据。
  • skipUntil订阅原始的Observable,但是忽略它的发射物,直到第二个Observable发射了一项数据那一刻,它开始发射原始Observable。
  • skipWhile订阅原始的Observable,但是忽略它的发射物,直到你指定的某个条件变为false的那一刻,它开始发射原始Observable。

RxJava中对应的是skipUntil,它默认不在任何特定的调度器上执行。



上图输出最后结果:4,5

2.4 first / last / firstOrDefault / lastOrDefault

first / firstOrDefault : 如果Observable有数据则只emit第一个数据,如果没有数据则emit默认数据。使用Take(1)或者ElementAt(0)是一样的效果
last / lastOrDefault : 如果Observable有数据则只emit最后一个数据,如果没有数据则emit默认数据。

last/first可以接受一个函数,用于判断是否是最后一个/第一个数据,上图最后结果:A5

你可以选择使用first(),你也可以选择使用takeFirst()。这两种调用的区别是当没有可用资源的时候,first()会抛出一个NoSuchElementException的异常,然而,takeFirst()只会简单的结束。

你使用哪种就看你是否需要明确的处理数据是否用完。

2.5 elementAt / elementAtOrDefault

如果我们只想要可观测序列发射的第五个元素该怎么办?elementAt()函数仅从一个序列中发射第n个元素然后就完成了。
如果我们想查找第五个元素但是可观测序列只有三个元素可供发射时该怎么办?我们可以使用elementAtOrDefault()。

2.6 ignoreElements

忽略所有的数据,只发射onCompleted和onError

2.7 sample

Sample操作符定时查看一个Observable,然后发射自上次采样以来它最近发射的数据。如果自上次采样以来,原始Observable没有发射任何数据,这个操作返回的Observable在那段时间内也不会发射任何数据。

2.8 timeout

假设我们工作的是一个时效性的环境,我们每隔两秒至少发射一个数据,那么我们可以使用timeout()函数来监听源可观测序列,就是在我们设定的时间间隔内如果没有得到一个值则发射一个错误。

2.9 distinct

distinct可以对 emit 的数据做去重处理


如上图最后的结果是: 1, 2, 3, 4, 6

2.10 distinctUntilChanged

根据一个函数产生的Key判定两个相邻的数据项是不是不同的。distinctUntilChanged也是去重,但是他有这么一个使用场景,例如:每次我们获得一个新值,我们都会更新当前正在显示的界面。我们出于一些交互或者性能考虑,我们不想在每次值一样时更新数据。我们想忽咯掉重复的值并且在数据确实改变时才想得到通知。ditinctUntilChanged()过滤函数就能做到这一点。它能轻易的忽咯掉所有的重复并且只发射出确实相对上一个数据有改变的新的值。

2.11 debounce

用简单的话讲就是当N个结点发生的时间太靠近(即发生的时间差小于设定的值T),debounce就会自动过滤掉前N-1个结点。例如在做百度地址联想的时候,可以使用debounce减少频繁的网络请求,避免每输入(删除)一个字就做一次联想。


第一个参数是时间间隔
第二个参数是时间单位
debounce表示emit数据之后一定时间内没有其他数据出现才真正emit数据。
图示中emit黄球后,在规定时间内又emit绿球,则黄球不会被emit。

2.12 throttleFirst

emit一定周期内的第一个数据。可以用作防止按钮重复点击


上图连续点击按钮的时候,500毫秒内只会响应第一次点击。

2.13 throttleLast

emit一定周期内的最后一个数据。


上图中每隔500ms emit当时的最后一个数据,demo中最终结果: 2,6,7。

三、变换类

3.1 map

接收一个指定的Func对象然后将它应用到每一个由Observable发射的值上。


上图把我们需要的一个对象的集合取出并返回

3.2 flatMap

解决嵌套回调(callback hell)问题,在复杂的场景中,我们有一个这样的Observable,它发射一个数据序列,这些数据本身也可以发射Observable0 RxJava的flatMap()函数提供一种铺平序列的方式,然后合并这些Observables发射的数据,最后将合并后的结果作为最终的Observable。当我们在处理可能有大量的Observables时,重要是记住任何一个Observables发生错误的情况,flatMap()将会触发它自己的onError()函数并放弃整个链。重要的一点提示是关于合并部分:它允许交又,这意味着flatMap()不能够保证在最终生成的Observable中源Observables确切的发射顺序。

3.3 concatMap

concatMap()函数解决了flatMap()的交又问题,提供了一种能够把 发射的值连续在一起的铺平函数,而不是合并它们。

3.4 flatMapIterable

flatMaplnterable()和flatMap()很像。仅有的本质不同是它将源数据两两结成对并生成Iterable,而不是原始数据项和生成的Observables。

3.5 switchMap

switchMap()和flatMap()很像,除了一点,每当源Observable发射一个新的数据项(Observable)时,它将取消订阅并停止监视之前那个数据项产生的Observable,并升始监视当前发射的这一个。

3.6 scan

scan()函数可以看做是一个累积函数。scan()函数对原始Observable发射的每一项数据都应用一个函数,计算出函数的结果z值,并将该值填 充回可观测序列,等待和下一次发射的数据一起使用。作为一个通用的例子,给出一个累加器:


demo中依次输出1,3,6,10,即依次把Func2作用在前一个输出结果和当前数据上。

3.7 groupBy

GroupBy操作符将原始Observable发射的数据按照key来拆分成一些小的Observable,然后这些小的Observable分别发射其所包含的的数据,类似于sql里面的groupBy。在使用中,我们需要提供一个生成key的规则,所有key相同的数据会包含在同一个小的Observable种。另外我们还可以提供一个函数来对这些数据进行转化,有点类似于集成了flatMap。举个例子,我们希望对一组数据进行排序。比如想按照最近更新日期,或者名称来排序数据时该怎么办,这个函数可以将源Observable变换成一个发射Observables的新的Observable。它们中的每一个新的Observable都发射一组指定的数据。

3.8 buffer

buffer()函数将源Observable变换一个新的Observable,这个新的Observable每次发射一组列表值而不是一个一个发射。下图中展示了buffer()如何将count作为一个参数来指定有多少数据项被包在发射的列表中:

buffer(count, skip)从原始Observable的第一项数据开始创建新的缓存,此后每当收到skip项数据,用count项数据填充缓存:开头的一项和后续的count-1项,它以列表(List)的形式发射缓存,取决于count和skip的值,这些缓存可能会有重叠部分(比如skip < count时),也可能会有间隙(比如skip > count时)。

buffer()带一个timespan的参数,会创建一个每隔timespan时间段就会发射一个列表的Observable。


第一个参数3表示,在emit数据之前,Observable需要缓存3个数据
第二个参数1表示,每次emit数据之后跳过1个数据。
最后结果分步骤如下:

1 - one, two, three
2 - two, three, four
3 - three, four, five
4 - four, five
5 - five

你可以使用Buffer操作符实现反压backpressure(意思是,处理这样一个Observable:它产生数据的速度可能比它的观察者消费数据的速度快)。Buffer操作符可以将大量的数据序列缩减为较少的数据缓存序列,让它们更容易处理。例如,你可以按固定的时间间隔,定期关闭和发射来自一个爆发性Observable的数据缓存。这相当于一个缓冲区。

3.9 window

window()函数和buffer()很像,但是它发射的是Observable而不是列表。下图展示了window()如何缓存3个数据项并把它们作为一个新的Observable发射出去。

这些Observables中的每一个都发射原始Observable数据的一个子集,数量由count指定,最后发射一个onCompleted()结束。正如buffer()一样,window()也有一个skip变体,如下图所示:这个window的变体立即打开它的第一个窗口。原始Observable每发射skip项数据它就打开一个新窗口(例如,如果skip等于3,每到第三项数据,它会打开一耳光新窗口)。每当当前窗口发射了count项数据,它就关闭当前窗口并打开一个新窗口。如果从原始Observable收到了onError或onCompleted通知它也会关闭当前窗口。如果skip=count,它的行为与window(source, count)相同;如果skip < count,窗口可会有count - skip 个重叠的数据;如果skip > count,在两个窗口之间会有skip - count项数据被丢弃。

3.10 cast

cast()函数是本章中最后一个操作符。它是map()操作符的特殊版本。它将源Observable中的每一项数据都转换为新的类型,把它变成了不同的Class

四、组合类

4.1 merge / mergeDelayError

merge: 合并多个数据源,merge 与concat不同的是把两个 Observable的数据合成一列数据,就像是从一个Observable emit,但是顺序不一定。例如一组数据来自网络,一组数据来自文件,需要合并两组数据一起展示。

mergeDelayError: 对于merge每个Observable抛出的错误都将会打断合并。如果你需要避免这种情况,RxJava提供了mergeDelayError(),它能从一个Observable中继续发射数据即使是其中有一个抛出了错误。当所有的Observables都完成时,mergeDelayError()将会发射onError() ,如下图所示:


例如上图的结果可能是:”A1”, “B1”, “A2”, “A3”, “A4”, “B2”, “B3”,但还有可能是其他的任何顺序

4.2 concat

concat用来组合多个Observable,并且维护了这些Observable的顺序,可以使用concat和first做缓存,依次检查memory、disk和network中是否存在数据,任何一步一旦发现数据后面的操作都不执行。


第一个参数为第一个Observable
第二个参数为第二个Observable
图示中连接两个Observable之后,数据会有序的连接起来,最后有序的emit数据,结果如下:
A1, A2, A3, A4, B1, B2, B3

4.3 zip / zipWith

Zip操作符返回一个Obversable,它使用这个函数按顺序结合两个或多个Observables发射的数据项,然后它发射这个函数返回的结果。它按照严格的顺序应用这个函数。它只发射与发射数据项最少的那个Observable一样多的数据。比如一个界面需要展示用户的一些信息, 而这些信息分别要从两个服务器接口中获取,而只有当两个都获取到了之后才能进行展示,这种并行的异步处理比较麻烦,不过用了 zip() 之后就会简单得多。

zipWith操作符总是接受两个参数,第一个参数是一个Observable或者一个Iterable。



上图中,在获取两个Observable的条目数据后,进行柔和,转成一个条目数据Item。

4.4 combineLatest

CombineLatest操作符行为类似于zip,但是只有当原始的Observable中的每一个都发射了一条数据时zip才发射数据。CombineLatest则在原始的Observable中任意一个发射了数据时发射一条数据。当原始Observables的任何一个发射了一条数据时,CombineLatest使用一个函数结合它们最近发射的数据,然后发射这个函数的返回值。例如注册的时候所有输入信息(邮箱、密码、电话号码等)合法才点亮注册按钮。

4.5 switchOnNext

给出一个发射多个Observables序列的源Observable,switchOnNext()订阅到源Observable然后升始发射由第一个发射的Observable发射的一样的数据。当源Observable发射一个新的Observable时,switchOnNext()立即取消订阅前一个发射数据的Observable(因此打断了从它那里发射的数据流)然后订阅一个新的Observable,并升始发射它的数据。

4.6 reduce

reduce 对所有数据进行处理,最终emit一个数据。


上图是对所有数据相加的结果,最后输出:10

五、错误处理

Catch操作符拦截原始Observable的onError通知,将它替换为其它的数据项或数据序列,让产生的Observable能够正常终止或者根本不终止。

RxJava将Catch实现为三个不同的操作符:

5.1 onErrorReturn

onErrorReturn方法返回一个镜像原有Observable行为的新Observable,后者会忽略前者的onError调用,不会将错误传递给观察者,作为替代,它会发发射一个特殊的项并调用观察者的onCompleted方法。

5.2 onErrorResumeNext

onErrorResumeNext方法返回一个镜像原有Observable行为的新Observable,后者会忽略前者的onError调用,不会将错误传递给观察者,作为替代,它会开始镜像另一个,备用的Observable。

5.3 onExceptionResumeNext

和onErrorResumeNext类似,onExceptionResumeNext方法返回一个镜像原有Observable行为的新Observable,也使用一个备用的Observable,不同的是,如果onError收到的Throwable不是一个Exception,它会将错误传递给观察者的onError方法,不会使用备用的Observable。

5.4 Retry

Retry操作符不会将原始Observable的onError通知传递给观察者,它会订阅这个Observable,再给它一次机会无错误地完成它的数据序列。Retry总是传递onNext通知给观察者,由于重新订阅,可能会造成数据项重复。

5.5 retryWhen

retryWhen和retry类似,区别是,retryWhen将onError中的Throwable传递给一个函数,这个函数产生另一个Observable,retryWhen观察它的结果再决定是不是要重新订阅原始的Observable。如果这个Observable发射了一项数据,它就重新订阅,如果这个Observable发射的是onError通知,它就将这个通知传递给观察者然后终止。retryWhen默认在trampoline调度器上执行,你可以通过参数指定其它的调度器。

六、辅助操作

do系列,注册一个动作作为原始Observable生命周期事件的一种占位符

6.1 doOnEach

doOnEach操作符让你可以注册一个回调,它产生的Observable每发射一项数据就会调用它一次。你可以以Action的形式传递参数给它,这个Action接受一个onNext的变体Notification作为它的唯一参数,你也可以传递一个Observable给doOnEach,这个Observable的onNext会被调用,就好像它订阅了原始的Observable一样。

6.2 doOnNext

doOnNext操作符类似于doOnEach(Action1),但是它的Action不是接受一个Notification参数,而是接受发射的数据项。

6.3 doOnSubscribe

doOnSubscribe操作符注册一个动作,当观察者订阅它生成的Observable它就会被调用。

6.4 doOnUnsubscribe

doOnUnsubscribe操作符注册一个动作,当观察者取消订阅它生成的Observable它就会被调用。

6.5 doOnCompleted

doOnCompleted 操作符注册一个动作,当它产生的Observable正常终止调用onCompleted时会被调用。

6.6 doOnError

doOnError 操作符注册一个动作,当它产生的Observable异常终止调用onError时会被调用。

6.7 doOnTerminate

doOnTerminate 操作符注册一个动作,当它产生的Observable终止之前会被调用,无论是正常还是异常终止。

6.8 doAfterTerminate

doAfterTerminate 操作符注册一个动作,当它产生的Observable终止之后会被调用,无论是正常还是异常终止。

6.9 TimeInterval

TimeInterval操作符拦截原始Observable发射的数据项,替换为发射表示相邻发射物时间间隔的对象。
RxJava中的实现为timeInterval,这个操作符将原始Observable转换为另一个Obserervable,后者发射一个标志替换前者的数据项,这个标志表示前者的两个连续发射物之间流逝的时间长度。新的Observable的第一个发射物表示的是在观察者订阅原始Observable到原始Observable发射它的第一项数据之间流逝的时间长度。不存在与原始Observable发射最后一项数据和发射onCompleted通知之间时长对应的发射物。

6.10 Timestamp

它将一个发射T类型数据的Observable转换为一个发射类型为Timestamped的数据的Observable,每一项都包含数据的原始发射时间。timestamp默认在immediate调度器上执行,但是可以通过参数指定其它的调度器。

如果你觉得好,对你有过帮助,请给我一点打赏鼓励吧,一分也是爱呀!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值