combineLatest
当2个(最少2个)Observables中的任何一个发射了数据时,使用一个聚合函数结合每个Observable发射的最近数据项,并且基于这个函数的结果来发射数据。
先看这个操作符需要的参数,因为这个操作符可以根据一个函数来结合每个Obserbable发射的数据,但它最多传入9个Observable,这里取样为最简单的2个Observable跟一聚合个函数:
- Observable<T1> o1 : Observable要连接对象1
- Observable <t2> o2 : Observable要连接对象2
- BiFunction<? super T1, ? super T2, ? extends R> combiner :用来连结 o1 ,o2的一个聚合函数。
这里是简写的对象类型,只因为是要这样用而写成这样好理解些。附上源码方法:
public static <T1, T2, R> Observable<R> combineLatest( ObservableSource<? extends T1> source1, ObservableSource<? extends T2> source2, BiFunction<? super T1, ? super T2, ? extends R> combiner) { ObjectHelper.requireNonNull(source1, "source1 is null"); ObjectHelper.requireNonNull(source2, "source2 is null"); return combineLatest(Functions.toFunction(combiner), bufferSize(), source1, source2); }
看简单的代码例子:
Observable<String> o1 = Observable.just("000", "111", "222");
Observable<String> o2 = Observable.just("qqq", "www","eee","rrr");
Observable.combineLatest(o1, o2, new BiFunction<String, String, String>() {
@Override
public String apply(String s, String s2) throws Exception {
return s + s2 + "--";
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d("combineLatest", "onSubscribe: ");
}
@Override
public void onNext(String s) {
Log.d("combineLatest", "onNext: " + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.d("combineLatest", "onComplete: ");
}
});
看输出结果:
04-23 16:29:56.783 3144-3144/com.example.testlink D/combineLatest: onSubscribe:
04-23 16:29:56.793 3144-3144/com.example.testlink D/combineLatest: onNext: 222qqq--
04-23 16:29:56.793 3144-3144/com.example.testlink D/combineLatest: onNext: 222www--
04-23 16:29:56.793 3144-3144/com.example.testlink D/combineLatest: onNext: 222eee--
04-23 16:29:56.793 3144-3144/com.example.testlink D/combineLatest: onNext: 222rrr--
04-23 16:29:56.793 3144-3144/com.example.testlink D/combineLatest: onComplete:
一开始没有看到输出结果,只看操作符介绍时猜测会产生12种输出结果,2个Observable各自发送了3,4个数据,按照组合排列应该是产生12种结果。但事实却打脸了,只有四种。至此,不得不在理解分析一下。看它的示意图:
看这个图跟最上面的介绍了应该是这样的,每个Observable发送一次数据都会拿到当前发射的这个数据跟另一个Observable最近发射的数据,通过这个聚合函数再来处理。因此上面的应该是o1先发射了所有数据源,然后再发射o2的数据源。当然对于o1最近的一个就是最后一个了。所以每次o2发射,聚合函数处理的2个数据源是o1的最后一个跟o2的所有数据了。对此我再修改一下o1,看运行结果是否这样,o1修改成这样:
Observable<Long> o1 = Observable.interval(1000, TimeUnit.MILLISECONDS).take(3);
对应的聚合函数修改:
Observable.combineLatest(o1, o2, new BiFunction<Long, String, String>() { @Override public String apply(Long s, String s2) throws Exception { return s + s2 + "--"; }
运行结果:
D/combineLatest: onSubscribe:
D/combineLatest: onNext: 0rrr--
D/combineLatest: onNext: 1rrr--
D/combineLatest: onNext: 2rrr--
D/combineLatest: onComplete:
o1每延迟一秒发射一个数据,共发射3个数据,而o2没有处理,最终o1会先o1发生完数据,等o1发生数据跟o2最近发射的数据是o2的最后一个数据。 聚合函数处理的2个数据源是o1的所有数据跟o2的最后一个数据。
但其实还有一个疑问,每次的结果都是o1或o2的最后一个数据跟另一个Observable的所有数据处理。那究竟是否可以认为combineLatest 总是2者之一的最后一个数据才会参与函数处理呢?还是因为发射效率过快导致的?为了证实一下这个问题这里再改变一下条件来看结果。首先将延迟时间缩短到可以忽略,最后确定时间为1毫秒,另一个发射的数据要足够多,为10个。最终结果:
D/combineLatest: onSubscribe:
D/combineLatest: onNext: 0qqq--
D/combineLatest: onNext: 0www--
D/combineLatest: onNext: 0eee--
D/combineLatest: onNext: 0rrr--
D/combineLatest: onNext: 1rrr--
D/combineLatest: onNext: 2rrr--
D/combineLatest: onNext: 2ttt--
D/combineLatest: onNext: 2yyy--
D/combineLatest: onNext: 2uuu--
D/combineLatest: onNext: 2iii--
D/combineLatest: onNext: 2ooo--
D/combineLatest: onNext: 2ppp--
D/combineLatest: onComplete:
结论:o1,o2是同步进行发射的,只是效率极快。而不是总是2者之一的最后一个数据才会参与函数处理。
join
引用来自翻译文档的解释:
任何时候,只要在另一个Observable发射的数据定义的时间窗口内,这个Observable发射了一条数据,就结合两个Observable发射的数据。
Join
操作符结合两个Observable发射的数据,基于时间窗口(你定义的针对每条数据特定的原则)选择待集合的数据项。你将这些时间窗口实现为一些Observables,它们的生命周期从任何一条Observable发射的每一条数据开始。当这个定义时间窗口的Observable发射了一条数据或者完成时,与这条数据关联的窗口也会关闭。只要这条数据的窗口是打开的,它将继续结合其它Observable发射的任何数据项。你定义一个用于结合数据的函数。
感觉看完也不是很理解。不急,看看方法参数,再写个例子,再回头理解理解。先看方法,方法需要传入4个参数:
- ObservableSource<? extends TRight> other : 参与结合函数的另一个Observable
- Function<? super T, ? extends ObservableSource<TLeftEnd>> leftEnd: 接收初始Observable的数据,返回时间窗口的Observables
- Function<? super TRight, ? extends ObservableSource<TRightEnd>> rightEnd: 接收other Observable的数据,返回时间窗口的Observables
- BiFunction<? super T, ? super TRight, ? extends R> resultSelector :结合函数
简单实现:
Observable<String> o1 = Observable.just("111", "222", "333");
Observable.just("aaa", "bbb", "ccc")
.join(o1, new Function<String, Observable<Long>>() {
@Override
public Observable<Long> apply(String s) throws Exception {
Log.d("join", "apply1: " + s);
return Observable.timer(10, TimeUnit.MILLISECONDS);
}
}, new Function<String, Observable<Long>>() {
@Override
public Observable<Long> apply(String s) throws Exception {
Log.d("join", "apply2: " + s);
return Observable.timer(10, TimeUnit.MILLISECONDS);
}
}, new BiFunction<String, String, String>() {
@Override
public String apply(String s, String s2) throws Exception {
return s + "_" + s2;
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.d("join", "onNext: " + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
输出结果:
D/join: apply1: aaa
D/join: apply1: bbb
D/join: apply1: ccc
D/join: apply2: 111
D/join: onNext: aaa_111
D/join: onNext: bbb_111
D/join: onNext: ccc_111
D/join: apply2: 222
D/join: onNext: aaa_222
D/join: onNext: bbb_222
D/join: onNext: ccc_222
D/join: apply2: 333
D/join: onNext: aaa_333
D/join: onNext: bbb_333
D/join: onNext: ccc_333
4个参数中只能第二个跟第三个稍微晦涩些,但是这2个又是相同逻辑的。解释里面说了,这2个Function是基于时间窗口,选择待集合的数据项。数据来源无非就2个Observable里发射的数据。然后时间窗口实现为Observables,这里就可以代入理解为一个定时器,在定时的这段时间内收集的数据,会参与第4个函数处理。从输出结果上理解:代码的时间窗口实现为一个timer,两个Observable的定时都是10毫秒。在10毫秒内,2个Observable都发射完了自己的数据,最终在结合函数处理下输出了2个Observable数据的所有的组合情况。2个Observable是同时进行的,基于各自的时间窗口收集的数据而产生不同的结果。
Concat
concat没有join跟combinelast那么复杂的操作。没有结合函数,没有时间窗口。它很简单,只需要传入Observable,然后订阅即可。Observable上限为4个,而且会按照参数顺序发射每个Observable里的数据。
Observable<String> o1 = Observable.just("111", "222", "333");
Observable<String> o2 = Observable.just("aaa", "bbb", "ccc");
Observable<Long> o3 = Observable.interval(1000, TimeUnit.MILLISECONDS).take(3);
Observable<String> o4 = Observable.just("x", "xx", "xxx");
Observable.concat(o1, o2,o3,o4).subscribe(new Consumer<Serializable>() {
@Override
public void accept(Serializable serializable) throws Exception {
Log.d("concat", "accept: "+serializable.toString());
}
});
按照顺序输出,即使中间有个延时Observable
D/concat: accept: 111
D/concat: accept: 222
D/concat: accept: 333
D/concat: accept: aaa
D/concat: accept: bbb
D/concat: accept: ccc
D/concat: accept: 0
D/concat: accept: 1
D/concat: accept: 2
D/concat: accept: x
D/concat: accept: xx
D/concat: accept: xxx
Merge
合并多个Observables的发射数据,好像它们是一个单个的Observable一样。和concat极其相似,但是merge可能会让合并的Observables发射的数据错乱,但是concat不会让数据交错,它会按顺序一个接着一个发射多个Observables的发射物。例子可以参考concat,这里不做演示。
Zip
将多个Observables(最多9个)的发射数据结合到一起,基于这个函数的结果为每个结合体发射单个数据项。它按照严格的顺序应用这个函数,一一对应。它只发射与发射数据项最少的那个Observable一样多的数据。
方法参数介绍:
- ObservableSource<? extends T1> source1 : 用于结合数据的Observable,可以有多个,最多9个;
- BiFunction<? super T1, ? super T2, ? extends R> zipper :结合函数
举个栗子:
Observable<String> o1 = Observable.just("111", "222", "333");
// Observable<String> o2 = Observable.just("aaa");
Observable<Long> o3 = Observable.interval(1000, TimeUnit.MILLISECONDS).take(2);
Observable.zip(o1, o3, new BiFunction<String, Long, Integer>() {
@Override
public Integer apply(String s, Long s2) throws Exception {
Log.d("zip", "apply: s= " + s + " s2= " + s2);
return (s + s2).length();
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d("zip", "accept: " + integer);
}
});
}
输出结果:
D/zip: apply: s= 111 s2= 0
D/zip: accept: 4
D/zip: apply: s= 222 s2= 1
D/zip: accept: 4
StartWith
在数据序列的开头插入一条指定的项,可以是一个Observable,也可以是一个发射数据。例子很简单:
Observable.just("111") .startWith("qqqq") .subscribe(new Consumer<String>() { @Override public void accept(String s) throws Exception { Log.d("startWith", "accept: " + s); } }); /** 输出结果: * D/startWith: accept: qqqq * D/startWith: accept: 111 */
switchOnNext
引用翻译文档的解释:
switchOnNext订阅一个发射多个Observables的Observable。它每次观察那些Observables中的一个,switchOnNext返回的这个Observable取消订阅前一个发射数据的Observable,开始发射最近的Observable发射的数据。注意:当原始Observable发射了一个新的Observable时(不是这个新的Observable发射了一条数据时),它将取消订阅之前的那个Observable。这意味着,在后来那个Observable产生之后到它开始发射数据之前的这段时间里,前一个Observable发射的数据将被丢弃(就像图例上的那个黄色圆圈一样)。
通俗点就是说: switchOnNext用来发射多个Observable的数据,如果Observable之间发射时机重叠了就忽略前一个Observable的未发射的数据,发射后一个Observable的数据,按照顺序直到最后。代码实例无(以后考虑补上)。