RxJava3.0 操作符之合并操作符使用
合并操作符可以将多个Observable进行组合.
合并操作符
Merge
merge/mergeArray/mergeWith
将多个可观察者合并成一个.通过使用 Merge 运算符,可以组合多个可观察量的输出,使它们像单个可观察对象一样工作。Merge合并的源Observable事件会交错输出.
merge 操作符支持的可观察者:
用法:
merge
最基本用法,就是调用静态方法 去合并多个Observable.将他们所有的数据项发出.
merge()
函数可支持合并 2-4个Observable,如果有更多的可以使用mergeArray()去实现.
除了直接合并Observable外,merge还支持两种合并
merge支持合并 一个数据类型是可观察对象的 迭代器(Iterable),将迭代器内部可观察对象像直接合并Observable那样合并.
merge支持合并 发射项是一个可观察对象 的Observable,将源Observable 的每个发射项 像直接合并Observable 那样去合并.
可观察者还有一个非静态的成员函数 mergeWith 去将自身与另一个Observable合并.(查看源码,其实内部还是调用静态merge函数,将自身与传递进来的Observable)
merge 合并的Observable 发射项的数据类型必须一致,或者继承自共同的父类
使用示例:
private void mergeOperator(){
Observable<String> ob1 = Observable.fromArray("1","2","3");
Observable<String> ob2 = Observable.just("a", "b");
//Observable 静态调用merge
Observable.merge(ob1,ob2).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "merge: " + s);
}
});
//输出:
//11051-11051/com.sky.rxjava I/sky>>>: merge: 1
//11051-11051/com.sky.rxjava I/sky>>>: merge: 2
//11051-11051/com.sky.rxjava I/sky>>>: merge: 3
//11051-11051/com.sky.rxjava I/sky>>>: merge: a
//11051-11051/com.sky.rxjava I/sky>>>: merge: b
//调用静态mergeArray 去合并多个Observable,结果与merge一致
Observable<String> ob3 = Observable.fromArray("4","5","6");
Observable<String> ob4 = Observable.just("x", "y");
Observable.mergeArray(ob1,ob2,ob3,ob4).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "mergeArray: " + s);
}
});
//merge 合并Observable类型的集合,拿出集合中的Observable去合并
List<Observable<String>> list = new ArrayList<>();
list.add(ob1);
list.add(ob2);
Observable.merge(list).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "merge Observable List: " + s);
}
});
//merge 合并源Observable 的发射项Observable,
//如下,结果是将源发射项的的每一项去进行合并.类似于对它所有的发射项做了merge合并.
Observable.merge(Observable.just(ob1,ob2)).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "merge Observable: " + s);
}
});
//Observable 对象也可调用其成员方法与另一个Observable进行合并,
//与merge不同的是,使用mergeWith去合并的另一个Observable 可观察的数据类型必须
//是与调用合并的Observable 一致或者其子类,而合并后的结果数据类型为调用方的数据类型,
ob1.mergeWith(ob2).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "merge with: " + s);
}
});
}
mergeDelayError/mergeArrayDelayError
与merge/mergeArray
使用基本一直,区别是:
merge操作时,任何一个原始Observable的onError
都通知会被立即传递给观察者,并终止合并后的Observable得后续发射.
而mergeDelayError/mergeArrayDelayError
会将错误通知先保留,等所有的正常数据发送完成之后,最后再发送onError
通知.
Zip
通过指定的函数将两个或多个可观察量发出的项集组合在一起,并根据此函数的结果发出项。zip按照严格的顺序应用这个函数。发射数量与发射数据项最小的Observable一致.多出的发射项被丢弃.
zip操作符支持的可观察者:
Rxjava中,zip()
支持 合并 2 -9 个Observable, 若有更多Observable需要合并可使用zipArray()
.
同样的,与merge相似,zip()
也支持对数据类型是Observable的迭代器进行合并. 还支持 对发射的数据项也是可观察对象 的Observable 的所有发射项进行合并.
也可使用Observable对象 调用其成员函数zipWith()
进行合并
zipWith()
传参是可观察对象时,调用的就是静态方法zip()
zipWith()
传参是迭代器时,相当于将源Observavle 发射项与 迭代器中的数据项按顺序调用函数组合,再发射.
zip()/zipArray()/zipWith()
默认不会对错误信息进行延迟处理,当其中一个发出Error
通知,就会立即终止.他们都提供了重载方法 设置delayError 用以控制是否延迟通知onError
,等所有数据合并技术,最后通知.
示例:
private void zipOperator(){
Observable<Long> ob1 = Observable.intervalRange(0, 5, 1, 1, TimeUnit.SECONDS);
Observable<String> ob2 =Observable.fromArray("A","B","C","D");
Observable<String> ob3 = Observable.just("x","y","z");
//zip 按顺序将每个Observable 对应位置的数据项进行合并,最后发送者合并都得数据项.
Observable.zip(ob1, ob2, ob3, new Function3<Long, String, String, String>() {
@Override
public String apply(Long aLong, String s, String s1) throws Throwable {
return aLong+s+s1;
}
}) .subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>> ", "zipOperator: " +s);
}
});
}
//输出:
//31611-31650/com.sky.rxjava I/sky>>>: zipOperator: 0Ax
//31611-31650/com.sky.rxjava I/sky>>>: zipOperator: 1By
//31611-31650/com.sky.rxjava I/sky>>>: zipOperator: 2Cz
CombineLatest
当要合并的两个可观察者对象中任意一个发出最新项时,使用指定的函数,将这个最新项与另一个可观察对象的最新项(之前发出的最后一个)进行计算组合,并将函数计算的结果发出.
如下图:
两个可观察者(下面简称ob1,ob2) 分别在不同时间点发出 1–5,A—D
ob1–>1,当发出1
时,另一个还未发出数据,所以不调用函数进行组合
ob2–>A,当发出A
时,取出ob1当前最新的数据项 1
,使用函数组合 1A
并发出
ob1–>2,发出2
时,取出ob2 当前最新数据 还是A
,组合2A
发出
ob2–>B,发出B
,取出ob1 最新数据2
,组合2B
ob2–> C,D, 当ob2 发出C
和D
时,ob1没再发出新数据,所以最新数据还是2
,分别组合2C
,2D
发出
ob1->3,4,5, 当ob1 发出3,4,5时ob2 所有数据已完毕,最新数据一直是D
,最后组合3D
,4D
,5D
发出
总结:每当有一个新项目被发出时,总是取出当前两个可观察者的最新项使用函数进行合并,并发出合并结果.
`
combineLatest操作符支持的可观察者:
例:
private void combineLatest(){
//定义两个可观察者,每间隔100ms发出一项,ob2设置50ms延迟,这样两个可观者的项是交替发出
//ob2只有三项,从11开始到13结束,所以ob2发完后,ob1还要再发出4,5两项才会结束
//在ob1发出4,5时,ob2已发送结束,所以最新数据永远都是13,所以最后组合输出的两项 分别是4---13,5---13
Observable<Long> ob1 = Observable.intervalRange(1, 5, 0, 100, TimeUnit.MILLISECONDS);
Observable<Long> ob2 = Observable.intervalRange(11, 3, 50, 100, TimeUnit.MILLISECONDS);
Observable.combineLatest(ob1, ob2, new BiFunction<Long, Long, String>() {
@Override
public String apply(Long l1, Long l2) throws Throwable {
Log.i(TAG, "apply: ob1 : "+ l1 +" , ob2 : " +l2);
return l1 +"---"+l2;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "combineLatest: "+ s);
}
});
}
输出:
18105-18148/com.sky.rxjava I/sky>>>: apply: ob1 : 1 , ob2 : 11
18105-18148/com.sky.rxjava I/sky>>>: combineLatest: 1---11
18105-18147/com.sky.rxjava I/sky>>>: apply: ob1 : 2 , ob2 : 11
18105-18147/com.sky.rxjava I/sky>>>: combineLatest: 2---11
18105-18148/com.sky.rxjava I/sky>>>: apply: ob1 : 2 , ob2 : 12
18105-18148/com.sky.rxjava I/sky>>>: combineLatest: 2---12
18105-18147/com.sky.rxjava I/sky>>>: apply: ob1 : 3 , ob2 : 12
18105-18147/com.sky.rxjava I/sky>>>: combineLatest: 3---12
18105-18148/com.sky.rxjava I/sky>>>: apply: ob1 : 3 , ob2 : 13
18105-18148/com.sky.rxjava I/sky>>>: combineLatest: 3---13
18105-18147/com.sky.rxjava I/sky>>>: apply: ob1 : 4 , ob2 : 13
18105-18147/com.sky.rxjava I/sky>>>: combineLatest: 4---13
18105-18147/com.sky.rxjava I/sky>>>: apply: ob1 : 5 , ob2 : 13
18105-18147/com.sky.rxjava I/sky>>>: combineLatest: 5---13
combineLatest 还有一个类似的 withLatestFrom,与combineLatest 不同的是,他是非静态成员方法,需要由可观察对象去调用,只有当这个调用方的可观察对象发出数据时,才去使用函数组合数据.
上面例子稍微改动
private void combineLatest(){
Observable<Long> ob1 = Observable.intervalRange(1, 5, 0, 100, TimeUnit.MILLISECONDS);
Observable<Long> ob2 = Observable.intervalRange(11, 3, 50, 100, TimeUnit.MILLISECONDS);
ob1.withLatestFrom(ob2, new BiFunction<Long, Long, String>() {
@Override
public String apply(Long aLong, Long aLong2) throws Throwable {
return aLong+"---"+ aLong2;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i("sky>>>", "withLatestFrom: "+ s);
}
});
}
输出:
19458-19499/com.sky.rxjava I/sky>>>: withLatestFrom: 2---11
19458-19499/com.sky.rxjava I/sky>>>: withLatestFrom: 3---12
19458-19499/com.sky.rxjava I/sky>>>: withLatestFrom: 4---13
19458-19499/com.sky.rxjava I/sky>>>: withLatestFrom: 5---13
ob1发出1时,ob2还未发出数据,所以略过,ob2发出所有数据时都略过,只有ob1发出时,才去取当时ob2的最新数据,进行组合发出.
此外还有,作用一目了然,就不再多做介绍
combineLatestDelayError():错误处理
combineLatestArray():多个可观察者
combineLatestArrayDelayError():多个可观察者错误处理
SwitchOnNext
将 发出项为Observables 的 Observable 转换为 一个单个的Observable,这个转换后的(Observable 简称 newOb
)发出项是 源Observable 当前发出的最近一次Observable发出的所有项.简单来讲,就是源Observable 一旦发出一项(Observable 简称 A
), 转换后的newOb
就去订阅这个A
,并将这个A的项依次发出,直到源Observable再次发出新项(Observable 简称B
),那么newOb
就会取消掉对A
的订阅,重新订阅B
,接下来newOb
就发出的是B
的项,直到源Observable再次发出一个新的项为止,重复取消原订阅,订阅新Observable的步骤.
注意:这里 newOb
的重新订阅发生在源Observable 发出一项的时候,而非发出的这项Observable发出数据的时候.
switchOnNext操作符支持的可观察者:
例:
private void switchOnNex() {
//创建了源Observable,它是一个每隔1秒就发出一项的可观察者,
//而它的每个子项又是一个Observable<String>类型的可观察,它是一个固定间隔时间发射,表明他是源Observable的第几项
Observable<Observable<String>> timeIntervals = Observable.intervalRange(1,5,0,1, TimeUnit.SECONDS)
.map(new Function<Long, Observable<String>>() {
@Override
public Observable<String> apply(Long aLong) throws Throwable {
return Observable.interval(10, 400, TimeUnit.MILLISECONDS).map(new Function<Long, String>() {
@Override
public String apply(Long l) throws Throwable {
return "outer : "+ aLong +" inner : "+ l;
}
});
}
});
//使用switchOnNext 对源Observable进行转换,并订阅
Observable.switchOnNext(timeIntervals).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i(TAG, "switchOnNext: " + s);
}
});
}
输出:
31521-31566/com.sky.rxjava I/sky>>>: switchOnNext: outer : 1 inner : 0
31521-31566/com.sky.rxjava I/sky>>>: switchOnNext: outer : 1 inner : 1
31521-31566/com.sky.rxjava I/sky>>>: switchOnNext: outer : 1 inner : 2
31521-31577/com.sky.rxjava I/sky>>>: switchOnNext: outer : 2 inner : 0
31521-31577/com.sky.rxjava I/sky>>>: switchOnNext: outer : 2 inner : 1
31521-31577/com.sky.rxjava I/sky>>>: switchOnNext: outer : 2 inner : 2
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 0
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 1
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 2
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 3
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 4
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 5
31521-31582/com.sky.rxjava I/sky>>>: switchOnNext: outer : 3 inner : 6
......
这里对这个输出进行解释一下: 当源Observable发出第一项时,第一项Observable 就被订阅,这时看到输出第一项的内容,而当1秒后,源Observable又
发出一项,这时第一项Observable被转换后的可观察对象取消订阅,重新订阅了最新一项,这时看到输出了源第二项的数据,又过一秒,源又发出第三项,转换后的可观察对象重新订阅此项,取消订阅上一项,这时源已经没有数据,这里就会一直输出第三项的数据.
此外 还有一个错误延迟的方法 switchOnNextDelayError()
StartWith
在源Observable 发射数据序列之前发出指定的项目序列.
startWith操作符支持的可观察者:
代码示例:
private void startOperator() {
Observable<String> ob = Observable.fromArray("a", "b", "c");
//在可观察对象ob之前 添加一个可观察对象的输出序列
ob.startWith(Observable.just("OK","Good")).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i(TAG, "startWith: " +s);
}
});
//输出
//6076-6076/com.sky.rxjava I/sky>>>: startWith: OK
//6076-6076/com.sky.rxjava I/sky>>>: startWith: Good
//6076-6076/com.sky.rxjava I/sky>>>: startWith: a
//6076-6076/com.sky.rxjava I/sky>>>: startWith: b
//6076-6076/com.sky.rxjava I/sky>>>: startWith: c
//在可观察对象ob之前 添加一条固定输出序列
ob.startWithItem("GG").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i(TAG, "startWithItem: " +s);
}
});
//输出:
//6076-6076/com.sky.rxjava I/sky>>>: startWithItem: GG
//6076-6076/com.sky.rxjava I/sky>>>: startWithItem: a
//6076-6076/com.sky.rxjava I/sky>>>: startWithItem: b
//6076-6076/com.sky.rxjava I/sky>>>: startWithItem: c
//在可观察对象ob之前 添加多条固定输出序列
ob.startWithArray("O","K").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i(TAG, "startWithArray: " + s);
}
});
//输出:
//6076-6076/com.sky.rxjava I/sky>>>: startWithArray: O
//6076-6076/com.sky.rxjava I/sky>>>: startWithArray: K
//6076-6076/com.sky.rxjava I/sky>>>: startWithArray: a
//6076-6076/com.sky.rxjava I/sky>>>: startWithArray: b
//6076-6076/com.sky.rxjava I/sky>>>: startWithArray: c
//在可观察对象ob之前 以集合形式添加多条固定输出序列
List<String> list = new ArrayList<>();
list.add("S");
list.add("K");
list.add("Y");
ob.startWithIterable(list).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
Log.i(TAG, "startWithIterable: " +s);
}
});
//输出:
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: S
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: K
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: Y
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: a
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: b
//6076-6076/com.sky.rxjava I/sky>>>: startWithIterable: c
}
Join
结合两个Observable发射的数据,基于时间窗口,每当一个Obsevable发出一个项目,这时候如果他在另一个Observale发出项目定义的时间窗口内,就将两个这两个项目进行组合并发出.
上面解释的比较抽象,下面根据join结合图再进行详细解释一番
如图所示,有两个Observable(下面 简称A和B),A
,B
都是间隔一段不等的时间发出一项,蓝色实线带箭头指向A
每一项时间窗口结束位置,A
发出每一项下面 黑色虚线箭头 到蓝色虚线箭头是它的时间窗口的持续期.同样,粉色实线箭头指向的B
每一项时间窗口结束位置,每一项黑色虚线箭头和粉色虚线箭头之间就是B
每一项的时间窗口持续期.当A
,B
任何一个发出一项时,此时检查是否在两一个Observable的某一项的时间窗口持续期内,如果在,就将他们合并并发出,如果不在,就不管.
下面是自己写的一个示例
private void joinOperator() {
//定义一个每隔2秒发送一项的Observable
Observable<Long> ob = Observable.intervalRange(1, 10, 0, 2, TimeUnit.SECONDS);
//定义一个延迟100ms后,每隔800毫秒发出一项的Observable
Observable<String> ob2 = Observable.intervalRange(11, 10, 100, 800, TimeUnit.MILLISECONDS)
.map(new Function<Long, String>() {
@Override
public String apply(Long aLong) throws Throwable {
return aLong + "";
}
});
ob.join(ob2, new Function<Long, ObservableSource<Long>>() {
@Override
public ObservableSource<Long> apply(Long s) throws Throwable {
//此处是对上面ob 的每一项定义一个时间窗口,对于ob的每一项,它的时间窗口持续时间是从它每一项发出开始
//到它定义的时间窗口Observable 发出一项或者完成,与此项关联的时间窗口就会关闭
//此处我们使用延迟 1500ms 的Observable,为ob的每一项定义了一个 1.5s持续时间窗口
return Observable.just(1L).delay(1500, TimeUnit.MILLISECONDS);
}
}, new Function<String, ObservableSource<Long>>() {
@Override
public ObservableSource<Long> apply(String s) throws Throwable {
//此处与上面类似,是为ob2 定义了一个0.8s 持续时间窗口
return Observable.just(1L).delay(800, TimeUnit.MILLISECONDS);
}
}, new BiFunction<Long, String, String>() {
@Override
public String apply(Long l, String s) throws Throwable {
//如果ob ,ob2 发出一项时,在另一个的某项的时间窗口持续期内,那么就会调用此处去进行合并,发出
return l + " : " + s;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
//最后此处就收到合并后的数据
Log.i(TAG, "join: " + s);
}
});
}
输出:
25956-26065/com.sky.rxjava I/sky>>>: join: 1 : 11
25956-26065/com.sky.rxjava I/sky>>>: join: 1 : 12
25956-26064/com.sky.rxjava I/sky>>>: join: 2 : 13
25956-26065/com.sky.rxjava I/sky>>>: join: 2 : 14
25956-26065/com.sky.rxjava I/sky>>>: join: 2 : 15
25956-26064/com.sky.rxjava I/sky>>>: join: 3 : 15
25956-26065/com.sky.rxjava I/sky>>>: join: 3 : 16
25956-26065/com.sky.rxjava I/sky>>>: join: 3 : 17
25956-26064/com.sky.rxjava I/sky>>>: join: 4 : 18
25956-26065/com.sky.rxjava I/sky>>>: join: 4 : 19
25956-26065/com.sky.rxjava I/sky>>>: join: 4 : 20
25956-26064/com.sky.rxjava I/sky>>>: join: 5 : 20
由于我们故意设置发送间隔时间不同,以及对两个Observable 每项定义窗口持续时间不同,所以就会出现以上输出,两次发出的项,在另外一个Observable同一项的时间窗口持续期内,就会出现重复的现象.