RxJava3.0 操作符之合并操作符使用

合并操作符可以将多个Observable进行组合.

合并操作符

rxjava 并发执行 rxjava合并_rxjava 并发执行

Merge

merge/mergeArray/mergeWith

将多个可观察者合并成一个.通过使用 Merge 运算符,可以组合多个可观察量的输出,使它们像单个可观察对象一样工作。Merge合并的源Observable事件会交错输出.

rxjava 并发执行 rxjava合并_java_02

merge 操作符支持的可观察者:

rxjava 并发执行 rxjava合并_开发语言_03

用法:

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通知.

rxjava 并发执行 rxjava合并_数据_04

Zip

通过指定的函数将两个或多个可观察量发出的项集组合在一起,并根据此函数的结果发出项。zip按照严格的顺序应用这个函数。发射数量与发射数据项最小的Observable一致.多出的发射项被丢弃.

rxjava 并发执行 rxjava合并_rxjava 并发执行_05

zip操作符支持的可观察者:

rxjava 并发执行 rxjava合并_ide_06

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 发出CD时,ob1没再发出新数据,所以最新数据还是2,分别组合2C,2D发出

ob1->3,4,5, 当ob1 发出3,4,5时ob2 所有数据已完毕,最新数据一直是D,最后组合3D,4D,5D发出

总结:每当有一个新项目被发出时,总是取出当前两个可观察者的最新项使用函数进行合并,并发出合并结果.

`

rxjava 并发执行 rxjava合并_数据_07

combineLatest操作符支持的可观察者:

rxjava 并发执行 rxjava合并_rxjava 并发执行_08

例:

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操作符支持的可观察者:

rxjava 并发执行 rxjava合并_java_09

rxjava 并发执行 rxjava合并_java_10

例:

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 发射数据序列之前发出指定的项目序列.

rxjava 并发执行 rxjava合并_java_11

startWith操作符支持的可观察者:

rxjava 并发执行 rxjava合并_java_12

代码示例:

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的某一项的时间窗口持续期内,如果在,就将他们合并并发出,如果不在,就不管.

rxjava 并发执行 rxjava合并_java_13

下面是自己写的一个示例

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同一项的时间窗口持续期内,就会出现重复的现象.