End

RxJava 组合操作符 merge concat flatMap zip join

本文地址


目录

RxJava 组合操作符 merge concat flatMap zip join

demo地址
参考

最佳使用场景

  • merge:同时发出多个互不相干的网络请求(异步并发执行),但每个网络请求返回的数据都会做同样的操作
  • concat:前一个网络请求返回后才开始下一个网络请求(同步顺序执行),例如三级缓存
  • flatMap:后一个网络请求的发起依赖于前一个网络请求的返回结果
  • zip:同时发出多个互不相干的网络请求,且只有全部返回后才处理最终合并的结果

merge mergeWith

merge

合并多个Observables的发射物

使用merge操作符你可以将多个Observables的输出合并,就好像它们是一个单个的Observable一样。

merge可能会让合并的Observables发射的数据交错(有一个类似的操作符concat不会让数据交错,它会按顺序一个接着一个发射多个Observables的发射物)。

除了传递多个Observable给merge,你还可以传递一个Observable序列,merge将合并它们的输出作为单个Observable的输出。

如果你传递一个发射Observables序列的Observable,你可以指定merge应该同时订阅的Observable的最大数量。一旦达到订阅数的限制,它将不再订阅原始Observable发射的任何其它Observable,直到某个已经订阅的Observable发射了onCompleted通知。

  • 一个 int 参数时代表 maxConcurrency:the maximum number of ObservableSources that may be subscribed to concurrently 同时
  • 两个 int 参数时后一个代表 bufferSize:the number of items to prefetch预取 from each inner ObservableSource

mergeWith

除了merge外,还提供了mergeWith方法,merge是静态方法,mergeWith是对象方法,例如Observable.merge(odds,evens)等价于odds.mergeWith(evens)

mergeDelayError

如果传递给merge的任何一个的Observable发射了onError通知终止了,merge操作符生成的Observable也会立即以onError通知终止。如果你想让它继续发射数据,在最后才报告错误,可以使用mergeDelayError

因为可能有多个merged的Observable遇到错误,所以mergeDelayError可能会在onError通知中传递多个错误的信息(但它不会多次调用观察者的onError方法)。因此,如果您想知道这些错误的nature,您应该编写观察者的onError方法,以便它们接受CompositeException类的参数。

案例

基础用法:合并数据

Observable<String> o1 = Observable.just("【1】", "【2】").delay(500, TimeUnit.MILLISECONDS);
Observable<String> o2 = Observable.just("【a】", "【b】").delay(300, TimeUnit.MILLISECONDS);
Observable.merge(o1, o2).subscribe(s -> log("merge:" + s));
Observable.concat(o1, o2).delay(1, TimeUnit.SECONDS).subscribe(s -> log("concat:" + s));

打印结果为:

merge:【a】,11:06:45 679,false
merge:【b】,11:06:45 688,false
merge:【1】,11:06:45 877,false
merge:【2】,11:06:45 883,false

concat:【1】,11:06:46 879,false
concat:【2】,11:06:46 886,false
concat:【a】,11:06:47 186,false
concat:【b】,11:06:47 192,false

merge:并发收集数据

使用 merge 操作符可以实现收集多个互不相干的数据源中的数据的效果。

log("开始");
Observable<String> obs1 = Observable.just("包青天").delay(800, TimeUnit.MILLISECONDS);//模拟网络请求
Observable<Integer> obs2 = Observable.just(28).delay(300, TimeUnit.MILLISECONDS);
Observable<String> obs3 = Observable.just("Android开发工程师").delay(600, TimeUnit.MILLISECONDS);

Observable.merge(obs1, obs2, obs3) //同时发出多个互不相干的网络请求,但每个网络请求返回的数据都会做同样的操作
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(serializable -> log("返回数据:" + serializable)); //并不能很好的区分是哪个数据源发送的数据
log("结束");

打印结果为:

开始,11:21:42 225,true
结束,11:21:42 304,true
返回数据:28,11:21:42 611,true  //无序
返回数据:Android开发工程师,11:21:42 910,true
返回数据:包青天,11:21:43 108,true //总耗时为三个中耗时最长的那个请求耗时的时间

如果上述案例中改为使用concat,则打印结果为:

开始,11:24:45 816,true
结束,11:24:45 889,true
返回数据:包青天,11:24:46 696,true //有序
返回数据:28,11:24:47 000,true
返回数据:Android开发工程师,11:24:47 606,true  //总耗时为三个请求所耗时累加在一起的时间

相比于 merge,concat 是用来保证"有序"的,如果为互不相干的数据源,即不需要保证有序,则应该使用 merge 而不是 concat。

concat:同步顺序执行

比如对图片缓存进行检查的逻辑,按照顺序检查内存缓存、磁盘缓存、网络缓存,如果前面的缓存中有数据则立即返回(需配合使用 first 操作符):

long start = System.currentTimeMillis();
Observable.concat(getObservable("内存缓存"), getObservable("磁盘缓存"), getObservable("网络缓存"))
    .first("默认数据") //the default item to emit if the source ObservableSource doesn't emit anything
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(s -> log("结果来自:" + s + ",耗时:" + (System.currentTimeMillis() - start) + "毫秒"));
private Observable<String> getObservable(String text) {
    return Observable.create(emitter -> {
        SystemClock.sleep(1000);//模拟耗时操作
        if (new Random().nextBoolean()) emitter.onNext(text);
        emitter.onComplete();
    });
}

打印结果可能为以下四种:

结果来自:内存缓存,耗时:1015毫秒,00:09:32 886,true
结果来自:磁盘缓存,耗时:2005毫秒,00:09:50 857,true
结果来自:网络缓存,耗时:3011毫秒,00:10:02 950,true
结果来自:默认数据,耗时:3009毫秒,00:09:53 438,true

打包:zip

通过一个函数将多个Observables的发射物结合到一起,基于这个函数的结果为每个结合体发射单个数据项。

打包,合并多个数据流,将多个Observable发射的数据组合在一起,然后将最终合并的数据作为单项数据发射

Returns an Observable that emits the results of a specified combiner function applied to combinations of two items emitted, in sequence, by two other Observables.

zip合并后的数组长度是两个数组长度最小的那一个数组的长度;而且我们可以自己定义合并的方式,即合并的算法自己决定

案例一
例如如下需求,界面显示的数据需要请求多个接口(并发请求),并且需要等到多个接口全部取完数据后再更新:

long start = System.currentTimeMillis();
Observable<String> obs1 = Observable.just("包青天").delay(800, TimeUnit.MILLISECONDS);//模拟网络请求
Observable<Integer> obs2 = Observable.just(28).delay(300, TimeUnit.MILLISECONDS);
Observable<String> obs3 = Observable.just("Android开发工程师").delay(600, TimeUnit.MILLISECONDS);

Observable.zip(obs1, obs2, obs3, MyZipBean::new)//(name, age, job) -> new MyZipBean(name, age, job)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(zipBean -> log(zipBean.toString() + ",耗时:" + (System.currentTimeMillis() - start) + "毫秒"));

MyZipBean 是定义的一个偶同的数据结构:

data class MyZipBean(val name: String, val age: Integer, val job: String)

打印结果为:

包青天,28,Android开发工程师,耗时:864毫秒,14:08:15 813,true  //总耗时为三个中耗时最长的那个请求耗时的时间:1.5秒

join

任何时候,只要在另一个Observable发射的数据定义的时间窗口内,这个Observable发射了一条数据,就结合两个Observable发射的数据。

Join操作符结合两个Observable发射的数据,基于时间窗口(你定义的针对每条数据特定的原则)选择待集合的数据项。你将这些时间窗口实现为一些Observables,它们的生命周期从任何一条Observable发射的每一条数据开始。当这个定义时间窗口的Observable发射了一条数据或者完成时,与这条数据关联的窗口也会关闭。只要这条数据的窗口是打开的,它将继续结合其它Observable发射的任何数据项。你定义一个用于结合数据的函数。

2016-06-14

posted @ 2016-06-14 14:14  白乾涛  阅读(4261)  评论(0编辑  收藏  举报