rxjava2组合运算符
使用多个源Observable创建单个Observable的运算符
Merge
通过合并它们的排放,将多个Observable组合成一个。合并可以交错合并的Observables发出的项目。并且任何源Observable的onError通知将立即传递给观察者并终止合并的Observable。
merge合并,就像多个进水管往水池注水,最终合成一个Observable。简单来说就是多输入,单输出。
举个例子,将两个Observable的数据在一起打印:
private void doSomeWork() {
Observable.merge(Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("0");
Thread.sleep(100);
observableEmitter.onNext("2");
Thread.sleep(100);
observableEmitter.onNext("4");
Thread.sleep(100);
observableEmitter.onNext("6");
Thread.sleep(100);
observableEmitter.onComplete();
}
}).subscribeOn(Schedulers.io()), Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("1");
Thread.sleep(50);
observableEmitter.onNext("3");
Thread.sleep(50);
observableEmitter.onNext("5");
Thread.sleep(50);
observableEmitter.onNext("7");
Thread.sleep(50);
observableEmitter.onComplete();
}
}).subscribeOn(Schedulers.io())).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, " accept : s : " + s);
}
});
}
打印结果
accept : s : 0
accept : s : 1
accept : s : 3
accept : s : 2
accept : s : 5
accept : s : 7
accept : s : 4
accept : s : 6
上面打印结果可以发现,发射的数据被交叉合并到一个Observable里面。但是如果去掉subscribeOn(Schedulers.io())),同步的合并Observable,它们将连接在一起并且不会交叉。其实如果想不交错的合并,也可以使用运算和聚合运算符中的Concat运算符。
我们有可能遇到这种问题,需要merge的多个Observable中,有一个发射了Error,比如多个请求接口数据共同展示一个页面,但是其中一个接口先请求得到结果而且是Error,这时候merge会失败异常。我们其实需要将异常放在这几个接口之后处理,则merge不能很好的满足需求。不要担心,rx提供了另外一个mergeDelayError操作符,可以实现。
MergeDelayError
通过合并它们的排放,将多个Observable组合成一个。合并可以交错合并的Observables发出的项目。遇到onError通知先保留onError通知,直到所有合并的Observable完成,然后才将它传递给观察者:
将上面的例子改下,我们看看结果:
private void doSomeWork() {
Observable.mergeDelayError(Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("0");
Thread.sleep(100);
observableEmitter.onError(new Throwable("error"));
}
}).subscribeOn(Schedulers.io()), Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("1");
Thread.sleep(50);
observableEmitter.onNext("3");
Thread.sleep(50);
observableEmitter.onNext("5");
Thread.sleep(50);
observableEmitter.onNext("7");
Thread.sleep(50);
observableEmitter.onComplete();
}
}).subscribeOn(Schedulers.io())).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(String s) {
Log.d(TAG, " onNext : s : " + s);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, " onError : throwable : " + throwable.getMessage());
}
@Override
public void onComplete() {
}
});
}
打印结果
onNext : s : 0
onNext : s : 1
onNext : s : 3
onNext : s : 5
onNext : s : 7
onError : throwable : error
Zip
通过指定的功能将多个Observable的发射组合在一起,并根据此功能的结果为每个组合发出单个项目。
Zip方法返回一个Observable,它将您选择的函数按顺序应用于两个(或更多)其他Observable发出的项的组合,此函数的结果将成为返回的Observable发出的项。 它以严格的顺序应用此函数,因此新Observable发出的第一个项将是应用于Observable 1发出的第一个项的函数和Observable 2发出的第一个项的结果。 新的zip-Observable发出的第二个项目是应用于Observable 1发出的第二个项目和Observable 2发出的第二个项目的结果,以此类推。 它只会发出与发出最少项目的源Observable发出的项目数一样多的项目。
zip组合是按顺序一一对应的,所以zip后的项目数等于最少项目的源Observable的项目数。
举个例子,将姓名与性别组合成一个人:
public class Person {
String name = "";
String sex = "";
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
public class Name {
String name = "";
public Name(String name) {
this.name = name;
}
}
public class Sex {
String sex = "";
public Sex(String sex) {
this.sex = sex;
}
}
private void doSomeWork() {
Observable.zip(Observable.just(new Name("张三"), new Name("李四"), new Name("王五"), new Name("赵六")),
Observable.just(new Sex("男"), new Sex("女"), new Sex("不男不女")),
new BiFunction<Name, Sex, Person>() {
@Override
public Person apply(Name name, Sex sex) throws Exception {
return new Person(name.name, sex.sex);
}
}).subscribe(new Consumer<Person>() {
@Override
public void accept(Person person) throws Exception {
Log.d(TAG, " accept : person : " + person.name + "," + person.sex);
}
});
}
由于姓名的数目与性别的数目不相等,所以最后zip的结果应该与最小的源Observable数目相等,一一组合后,打印结果:
accept : person : 张三,男
accept : person : 李四,女
accept : person : 王五,不男不女
Join
根据另一个Observable发出的项目定义的时间窗口期间发出一个Observable中的项目时,组合两个Observable发出的项目。
Join运算符组合两个Observable发出的项目,并根据您在每个项目上定义的持续时间窗口选择要组合的项目。 您将这些窗口实现为Observables,其生命周期以Observable发出的每个项目开始。 当这样的窗口定义Observable发出项目或完成时,与其关联的项目的窗口将关闭。 只要项目的窗口打开,它就会与其他Observable发出的任何项目组合在一起。 您可以定义项目组合的功能。
join操作符其实平时基本上也没怎么用到,想理解join,先看下他的几个参数:
Observable:源Observable需要组合的Observable,这里我们姑且称之为目标Observable;
Func1:接收从源Observable发射来的数据,并返回一个Observable,这个Observable的声明周期决定了源Obsrvable发射出来的数据的有效期;
Func1:接收目标Observable发射来的数据,并返回一个Observable,这个Observable的声明周期决定了目标Obsrvable发射出来的数据的有效期;
Func2:接收从源Observable和目标Observable发射出来的数据,并将这两个数据组合后返回。
join操作符的源Observable和目标Observable他们都按照自己的节奏发射数据,发射数据的有效期由对应的Func决定。目标Observable每发射一个数据,都把它和源Observable中已经发射的未失效的数据进行一对一匹配。
举个例子:
public class Person {
String name = "";
String sex = "";
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
public class Name {
String name = "";
public Name(String name) {
this.name = name;
}
}
public class Sex {
String sex = "";
public Sex(String sex) {
this.sex = sex;
}
}
private void doSomeWork() {
Observable.interval(50, TimeUnit.MILLISECONDS).take(4).map(new Function<Long, Name>() {
@Override
public Name apply(Long aLong) throws Exception {
if (0 == aLong)
return new Name("张三");
else if (1 == aLong)
return new Name("李四");
else if (2 == aLong)
return new Name("王五");
else
return new Name("赵六");
}
}).join(Observable.interval(50, TimeUnit.MILLISECONDS).take(3).map(new Function<Long, Sex>() {
@Override
public Sex apply(Long aLong) throws Exception {
if (0 == aLong)
return new Sex("男");
else if (1 == aLong)
return new Sex("女");
else
return new Sex("不男不女");
}
}), new Function<Name, ObservableSource<Long>>() {
@Override
public ObservableSource<Long> apply(Name name) throws Exception {
return Observable.timer(100, TimeUnit.MILLISECONDS);
}
}, new Function<Sex, ObservableSource<Long>>() {
@Override
public ObservableSource<Long> apply(Sex sex) throws Exception {
return Observable.timer(50, TimeUnit.MILLISECONDS);
}
}, new BiFunction<Name, Sex, Person>() {
@Override
public Person apply(Name name, Sex sex) throws Exception {
return new Person(name.name, sex.sex);
}
}).subscribe(new Consumer<Person>() {
@Override
public void accept(Person person) throws Exception {
Log.d(TAG, " accept : person : " + person.name + "," + person.sex);
}
});
}
打印结果
accept : person : 张三,男
accept : person : 李四,男
accept : person : 张三,女
accept : person : 李四,女
accept : person : 王五,女
accept : person : 张三,不男不女
accept : person : 李四,不男不女
accept : person : 王五,不男不女
accept : person : 赵六,不男不女
在这里对比下merge、zip、join这三个操作符:
操作符 | 说明 |
---|---|
merge | 合并多个Observable发出的数据 ,最终发射的数目等于源数目总和; |
zip | 按顺序组合多个Obserable发出数据内容,最终发射的数目由最小数目的源Observable决定; |
join | 两个源Obserable据自己的节奏不断发射数据元素,第二个数据源B,每发射一个数据,都和第一个数据源A中已经发射的未失效数据进行一对一匹配,最终发射的数目看时间情况; |
CombineLatest
当一个项目由两个Observable中的任何一个发出时,通过指定的函数组合每个Observable发出的最新项目,并根据此函数的结果发出项目。
combineLatest操作符是每个源发射出后都会和另外一个源最后发出的项进行组合。如上图,A发射后,另一个源最后发出的是1,所以组合成1A;再看看3发射出去的时候,与之对应的另一源最后发出的是D,所以最后组合成的是3D。
举个例子:
public class Person {
String name = "";
String sex = "";
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
public class Name {
String name = "";
public Name(String name) {
this.name = name;
}
}
public class Sex {
String sex = "";
public Sex(String sex) {
this.sex = sex;
}
}
private void doSomeWork() {
Observable.combineLatest(Observable.interval(20, TimeUnit.MILLISECONDS).take(4).map(new Function<Long, Name>() {
@Override
public Name apply(Long aLong) throws Exception {
if (0 == aLong)
return new Name("张三");
else if (1 == aLong)
return new Name("李四");
else if (2 == aLong)
return new Name("王五");
else
return new Name("赵六");
}
}), Observable.interval(50, TimeUnit.MILLISECONDS).take(3).map(new Function<Long, Sex>() {
@Override
public Sex apply(Long aLong) throws Exception {
if (0 == aLong)
return new Sex("男");
else if (1 == aLong)
return new Sex("女");
else
return new Sex("不男不女");
}
}), new BiFunction<Name, Sex, Person>() {
@Override
public Person apply(Name name, Sex sex) throws Exception {
return new Person(name.name, sex.sex);
}
}).subscribe(new Consumer<Person>() {
@Override
public void accept(Person person) throws Exception {
Log.d(TAG, " accept : person : " + person.name + "," + person.sex);
}
});
}
当张三发射出来的时候,第二个源还没有发射出项目,等第二个源发射出男时,第一个源已经在前发射出李四,所以第一个打印的是李四,男。打印结果:
accept : person : 李四,男
accept : person : 王五,男
accept : person : 赵六,男
accept : person : 赵六,女
accept : person : 赵六,不男不女
StartWith
在开始从源Observable中发出项之前,发出指定的项序列。
StartWith这个操作符比较简单,顾名思义就是在发射源项目前先发射with中的项目。如果需要在前发射多个项目用startWithArray操作符即可。另一方面,如果要将一系列项追加到源发出的项的末尾,则需要Concat运算符。
举个例子:
private void doSomeWork() {
Observable.range(0, 3)
.startWith(-1)
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, " accept : integer : " + integer);
}
});
}
打印结果
accept : integer : -1
accept : integer : 0
accept : integer : 1
accept : integer : 2
参考资料,参考但不局限以下链接
http://reactivex.io/documentation/operators.html#transforming
https://blog.youkuaiyun.com/peaty34/article/details/52247784
https://www.jianshu.com/p/546fe44a6e22