前言
上一篇文章我們學習了過濾類操作符,本篇我們將一起來學習RxJava組合類操作符。組合操作符主要是用來同時處理多個Observable,將他們進行組合建立出新的滿足我們需求的Observable,一起來看下都有哪些。
組合操作符
Merge
merge操作符,將兩個Observable要發射的觀測序列合併為一個序列進行發射。按照兩個序列每個元素的發射時間先後進行排序,同一時間點發射的元素則是無序的。
//將一個傳送字母的Observable與傳送數字的Observable合併發射
final String[] words = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I"};
//字母Observable,每200ms發射一次
Observable<String> wordSequence = Observable.interval(200, TimeUnit.MILLISECONDS)
.map(new Func1<Long, String>() {
@Override
public String call(Long position) {
return words[position.intValue()];
}
})
.take(words.length);
//數字Observable,每500ms發射一次
Observable<Long> numberSequence = Observable.interval(500, TimeUnit.MILLISECONDS).take(4);
Observable.merge(wordSequence, numberSequence)
.subscribe(new Action1<Serializable>() {
@Override
public void call(Serializable serializable) {
Log.e("rx_test", "merge:" + serializable.toString());
}
});複製程式碼
輸出結果:
merge:A
merge:B
merge:0
merge:C
merge:D
merge:E
merge:1
merge:F
merge:G
merge:2
merge:H
merge:I
merge:3複製程式碼
原理圖:
merge操作符還有一種入參merge(Observable[]),可傳入含有多個Observable的集合,merge操作符也可將這多個Observable的序列合併後發射。
MergeDelayError
mergeDelayError操作符,與merge功能類似,都是用來合併Observable的。不同之處在於mergeDelayError操作符在合併過程中發生異常的話不會立即停止合併,而會在所有元素合併發射完畢之後再發射異常。但發生異常的那個Observable就不會發射資料了。
//字母Observable,每200ms發射一次,模擬過程中產生一個異常
Observable<String> wordSequence = Observable.interval(200, TimeUnit.MILLISECONDS)
.map(new Func1<Long, String>() {
@Override
public String call(Long position) {
Long cache = position;
if (cache == 3) {
cache = cache / 0;
}
return words[position.intValue()];
}
})
.take(words.length);
//數字Observable,每500ms發射一次
Observable<Long> numberSequence = Observable.interval(500, TimeUnit.MILLISECONDS).take(4);
Observable.mergeDelayError(wordSequence, numberSequence)
.subscribe(new Action1<Serializable>() {
@Override
public void call(Serializable serializable) {
Log.e("rx_test", "mergeDelayError:" + serializable.toString());
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e("rx_test", "mergeDelayError:" + throwable.getMessage());
}
}, new Action0() {
@Override
public void call() {
Log.e("rx_test", "mergeDelayError:onComplete");
}
});複製程式碼
輸出結果:
mergeDelayError:A
mergeDelayError:B
mergeDelayError:0
mergeDelayError:C
mergeDelayError:1
mergeDelayError:2
mergeDelayError:3
mergeDelayError:divide by zero複製程式碼
由輸出結果可看出,wordSequence在發射到C時丟擲了一個異常,停止發射其剩下的資料,但合併沒有停止。合併完成之後這個異常才被髮射了出來。
原理圖:
Concat
concat操作符,將多個Obserbavle發射的資料進行合併後發射,類似於merge操作符。但concat操作符是將Observable依次發射,是有序的。
Observable<String> wordSequence = Observable.just("A", "B", "C", "D", "E");
Observable<Integer> numberSequence = Observable.just(1, 2, 3, 4, 5);
Observable<String> nameSequence = Observable.just("Sherlock", "Holmes", "Xu", "Lei");
Observable.concat(wordSequence, numberSequence, nameSequence)
.subscribe(new Action1<Serializable>() {
@Override
public void call(Serializable serializable) {
Log.e("rx_test", "concat:" + serializable.toString());
}
});複製程式碼
輸出結果:
concat:A
concat:B
concat:C
concat:D
concat:E
concat:1
concat:2
concat:3
concat:4
concat:5
concat:Sherlo
concat:Holmes
concat:Xu
concat:Lei複製程式碼
原理圖:
Zip
zip(Observable, Observable, Func2)操作符,根據Func2中的call()方法規則合併兩個Observable的資料項併發射。
注意:若其中一個Observable資料傳送結束或出現異常後,另一個Observable也會停止發射資料。
Observable<String> wordSequence = Observable.just("A", "B", "C", "D", "E");
Observable<Integer> numberSequence = Observable.just(1, 2, 3, 4, 5, 6);
Observable.zip(wordSequence, numberSequence, new Func2<String, Integer, String>() {
@Override
public String call(String s, Integer integer) {
return s + integer;
}
}).subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("rx_test", "zip:" + s);
}
});複製程式碼
輸出結果:
zip:A1
zip:B2
zip:C3
zip:D4
zip:E5複製程式碼
由輸出結果可看出numberSequence觀測序列最後的6並沒有發射出來,由於wordSequence觀測序列已發射完所有資料,所以組合序列也停止發射資料了。
原理圖:
StartWith
startWith操作符,用於在源Observable發射的資料前,插入指定的資料併發射。
Observable.just(4, 5, 6, 7)
.startWith(1, 2, 3)
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.e("rx_test", "startWith:" + integer);
}
});複製程式碼
輸出結果:
startWith:1
startWith:2
startWith:3
startWith:4
startWith:5
startWith:6
startWith:7複製程式碼
原理圖:
startWith還有兩種入參:
- startWith(Iterable):可在源Observable發射的資料前插入Iterable資料併發射。
- startWith(Observable):可在源Observable發射的資料前插入另一Observable發射的資料併發射。
SwitchOnNext
switchOnNext操作符,用來將一個發射多個小Observable的源Observable轉化為一個Observable,然後發射多個小Observable所發射的資料。若小Observable正在發射資料時,源Observable又發射了新的小Observable,則前一個小Observable還未發射的資料會被拋棄,直接發射新的小Observable所發射的資料,上例子。
```java
//每隔500ms產生一個Observable
Observable> observable = Observable.interval(500, TimeUnit.MILLISECONDS).map(new Func1<Long, Observable<Long>>() { @Override public Observable<Long> call(Long aLong) { //每隔200毫秒產生一組資料(0,10,20,30,40) return Observable.interval(200, TimeUnit.MILLISECONDS) .map(new Func1<Long, Long>() { @Override public Long call(Long aLong) { return aLong * 10; } }).take(5); } }).take(2);複製程式碼
Observable.switchOnNext(observable)
.subscribe(new Action1() {
@Override
public void call(Long aLong) {
Log.e("rx_test", "switchOnNext:" + aLong);
}
});
輸出結果:
```java
switchOnNext:0
switchOnNext:10
switchOnNext:0
switchOnNext:10
switchOnNext:20
switchOnNext:30
switchOnNext:40複製程式碼
由輸出結果發現第一個小Observable列印到10則停止了發射資料,說明其發射到10時,新的小Observable被建立了出來,第一個小Observable則被中斷髮射,開始發射新的小Observable的資料。
原理圖:
CombineLatest
combineLatest操作符,用於將兩個Observale最近發射的資料以Func2函式的規則進行組合併發射。
//引用merge的例子
final String[] words = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I"};
Observable<String> wordSequence = Observable.interval(300, TimeUnit.MILLISECONDS)
.map(new Func1<Long, String>() {
@Override
public String call(Long position) {
return words[position.intValue()];
}
})
.take(words.length);
Observable<Long> numberSequence = Observable.interval(500, TimeUnit.MILLISECONDS)
.take(5);
Observable.combineLatest(wordSequence, numberSequence,
new Func2<String, Long, String>() {
@Override
public String call(String s, Long aLong) {
return s + aLong;
}
})
.subscribe(new Action1<Serializable>() {
@Override
public void call(Serializable serializable) {
Log.e("rx_test", "combineLatest:" + serializable.toString());
}
});複製程式碼
輸出結果:
combineLatest:A0
combineLatest:B0
combineLatest:C0
combineLatest:C1
combineLatest:D1
combineLatest:E1
combineLatest:E2
combineLatest:F2
combineLatest:F3
combineLatest:G3
combineLatest:H3
combineLatest:H4
combineLatest:I4複製程式碼
如果將wordSequence與numberSequence的入參順序互換,輸出結果也會不同:
combineLatest:0A
combineLatest:0B
combineLatest:0C
combineLatest:1C
combineLatest:1D
combineLatest:2D
combineLatest:2E
combineLatest:2F
combineLatest:3F
combineLatest:3G
combineLatest:3H
combineLatest:4H
combineLatest:4I複製程式碼
wordSequence每300ms發射一個字元,numberSequence每500ms發射一個數字。可能有些碼友不知道這個輸出結果怎麼來的,這個操作符確實不太好理解。我們來看一下這個原理圖就很清楚了。
原理圖:
Join
join(Observable, Func1, Func1, Func2)操作符,類似於combineLatest操作符,用於將ObservableA與ObservableB發射的資料進行排列組合。但join操作符可以控制Observable發射的每個資料的生命週期,在每個發射資料的生命週期內,可與另一個Observable發射的資料按照一定規則進行合併,來看下join的幾個入參。
- Observable:需要與源Observable進行組合的目標Observable。
- Func1:接收從源Observable發射來的資料,並返回一個Observable,這個Observable的宣告週期決定了源Obsrvable發射出來的資料的有效期;
- Func1:接收目標Observable發射來的資料,並返回一個Observable,這個Observable的宣告週期決定了目標Obsrvable發射出來的資料的有效期;
- Func2:接收從源Observable和目標Observable發射出來的資料,並將這兩個資料按自定的規則組合後返回。
join操作符的組合方式類似於數學上的排列組合規則,以ObservableA為基準源Observable,按照其自身週期發射資料,且每個發射出來的資料都有其有效期。而ObservableB每發射出來一個資料,都與A發射出來的並且還在有效期內的資料按Func2函式中的規則進行組合,B發射出來的資料也有其有效期。最後再將結果發射給觀察者進行處理。//產生字母的序列,週期為1000ms String[] words = new String[]{"A", "B", "C", "D", "E", "F", "G", "H"}; Observable<String> observableA = Observable.interval(1000, TimeUnit.MILLISECONDS) .map(new Func1<Long, String>() { @Override public String call(Long aLong) { return words[aLong.intValue()]; } }).take(8); //產0,1,2,3,4,5,6,7的序列,延時500ms發射,週期為1000ms Observable<Long> observableB = Observable.interval(500, 1000, TimeUnit.MILLISECONDS) .map(new Func1<Long, Long>() { @Override public Long call(Long aLong) { return aLong; } }).take(words.length); //join observableA.join(observableB, new Func1<String, Observable<Long>>() { @Override public Observable<Long> call(String s) { //ObservableA發射的資料有效期為600ms return Observable.timer(600, TimeUnit.MILLISECONDS); } }, new Func1<Long, Observable<Long>>() { @Override public Observable<Long> call(Long aLong) { //ObservableB發射的資料有效期為600ms return Observable.timer(600, TimeUnit.MILLISECONDS); } }, new Func2<String, Long, String>() { @Override public String call(String s, Long aLong) { return s + aLong; } } ).subscribe(new Action1<String>() { @Override public void call(String s) { Log.e("rx_test", "join:" + s); } });複製程式碼
輸出結果:
原理圖:join:A0 join:A1 join:B1 join:B2 join:C2 join:C3 join:D3 join:D4 join:E4 join:E5 join:F5 join:F6 join:G6 join:G7 join:H7複製程式碼
GroupJoin
groupJoin操作符,類似於join操作符,區別在於第四個引數Func2的傳入函式不同,對join之後的結果包裝了一層小的Observable,便於使用者再次進行一些過濾轉換等操作再發射給Observable。
observableA.groupJoin(observableB,
new Func1<String, Observable<Long>>() {
@Override
public Observable<Long> call(String s) {
return Observable.timer(600, TimeUnit.MILLISECONDS);
}
},
new Func1<Long, Observable<Long>>() {
@Override
public Observable<Long> call(Long aLong) {
return Observable.timer(600, TimeUnit.MILLISECONDS);
}
},
new Func2<String, Observable<Long>, Observable<String>>() {
@Override
public Observable<String> call(final String s, Observable<Long> longObservable) {
return longObservable.map(new Func1<Long, String>() {
@Override
public String call(Long aLong) {
return s + aLong;
}
});
}
})
.subscribe(new Action1<Observable<String>>() {
@Override
public void call(Observable<String> stringObservable) {
stringObservable.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("rx_test", "groupJoin:" + s);
}
});
}
});複製程式碼
輸出結果:
groupJoin:A0
groupJoin:A1
groupJoin:B1
groupJoin:B2
groupJoin:C2
groupJoin:C3
groupJoin:D3
groupJoin:D4
groupJoin:E4
groupJoin:E5
groupJoin:F5
groupJoin:F6
groupJoin:G6
groupJoin:G7
groupJoin:H7複製程式碼
原理圖:
總結
到此,本篇關於RxJava的常用組合類操作符就講解完畢了。通過以上四篇文章對RxJava四類操作符的學習,相信大家已經基本掌握RxJava如何使用了。實踐是檢驗真理的唯一標準,下一篇我們來一起上專案看看實踐中如何使用RxJava。
技術渣一枚,有寫的不對的地方歡迎大神們留言指正,有什麼疑惑或者建議也可以在我Github上RxJavaDemo專案Issues中提出,我會及時回覆。
附上RxJavaDemo的地址:
RxJavaDemo