- 原文鏈接: RxJava Observable tranformation: concatMap() vs flatMap()
- 原文作者: Fernando Cejas
- 譯文出自: 小鄧子的簡書
- 譯者: 小鄧子
- 校對者: hi大頭鬼hi
- 狀態(tài): 完成
是時(shí)候回歸寫作了。(譯者注:原作者吧啦吧啦嘮家常,這里就不做翻譯了,但是,有兩個(gè)重要的鏈接,點(diǎn)我,再點(diǎn)我)
Observable 轉(zhuǎn)換
當(dāng)你有一個(gè)需要訂閱的Observable
,并且希望轉(zhuǎn)換結(jié)果的時(shí)候(切記,響應(yīng)式編程中一切皆流)。即將涉及到observable
轉(zhuǎn)換的時(shí)候,從隊(duì)列中取出將要消費(fèi)的事件,不可能一直是我們需要的格式或者形狀,可能每個(gè)值都需要擴(kuò)展成更豐富的對象或者化作更多的值。為了達(dá)到目的,我們可以為每一個(gè)observable
的返回值使用一個(gè)這樣的方法函數(shù),使用它可以將所有已發(fā)送的事件轉(zhuǎn)換成各種Observable
,并最終合并結(jié)果。不要擔(dān)心,不能馬上理解這種概念(關(guān)于響應(yīng)式,我也思考了一段時(shí)間),讓我們來看一個(gè)小栗子吧。
問題
我需要從數(shù)據(jù)庫檢索出一組數(shù)值,然后每個(gè)數(shù)值都要調(diào)用這樣的一個(gè)方法,它不僅支持異步轉(zhuǎn)換,還能維持之前的輸出順序。最后,將他們轉(zhuǎn)換成UI展示所需的列表。然而蛋疼的是,結(jié)果并不是我想要的,因?yàn)椋何沂褂昧艘粋€(gè)不能維持元素順序的操作符 Observable.flatMap()
。
簡單示例
讓我用一個(gè)簡單示例演示上面提到的事情。我們有一個(gè)能夠發(fā)送整型(對象)事件的Observable
,并且能夠計(jì)算每個(gè)值的平方和。
public class DataManager {
private final List<Integer> numbers;
private final Executor jobExecutor;
public DataManager() {
this.numbers = new ArrayList<>(Arrays.asList(2, 3, 4, 5, 6, 7, 8, 9, 10));
jobExecutor = JobExecutor.getInstance();
}
public Observable<Integer> getNumbers() {
return Observable.from(numbers);
}
public List<Integer> getNumbersSync() {
return this.numbers;
}
public Observable<Integer> squareOf(int number) {
return Observable.just(number * number).subscribeOn(Schedulers.from(this.jobExecutor));
}
}
這個(gè)DataManager
類有一個(gè)方法:能夠生成一個(gè)可以發(fā)送2到10的數(shù)字事件的Observable
。因此可以用這個(gè)方法計(jì)算每個(gè)值的平方和。
private final Func1<Integer, Observable<Integer>> SQUARE_OF_NUMBER =
new Func1<Integer, Observable<Integer>>() {
@Override public Observable<Integer> call(Integer number) {
return dataManager.squareOf(number);
}
};
把每個(gè)Integer
作為一個(gè)實(shí)體,生成一個(gè)Observable<Integer>
,合并,然后發(fā)送結(jié)果。如你所看到的,dataManager.squareOf()
是一個(gè)異步方法(為達(dá)到演示目的),看起來是這樣的:
public Observable<Integer> squareOf(int number) {
return Observable.just(number * number).subscribeOn(Schedulers.from(this.jobExecutor));
}
雖然這也能運(yùn)行,但并不是預(yù)期結(jié)果(至少不是我想要的),因?yàn)樵氐捻樞虮淮騺y了。
flatMap()與concatMap()的比較
這兩個(gè)方法似乎相差無幾,但有一點(diǎn)不同:用操作符合并最終結(jié)果的時(shí)候。這里有一些官網(wǎng)的東西:
flatMap()
操作符使用你提供的原本會(huì)被原始Observable
發(fā)送的事件,來創(chuàng)建一個(gè)新的Observable
。而且這個(gè)操作符,返回的是一個(gè)自身發(fā)送事件并合并結(jié)果的Observable
。可以用于任何由原始Observable
發(fā)送出的事件,發(fā)送合并后的結(jié)果。記住,flatMap()
可能交錯(cuò)的發(fā)送事件,最終結(jié)果的順序可能并是不原始Observable
發(fā)送時(shí)的順序。為了防止交錯(cuò)的發(fā)生,可以使用與之類似的concatMap()
操作符。
如你所見,這兩個(gè)方法非常的相似,只在形成輸出的時(shí)候存在微小的區(qū)別(在map()
操作符執(zhí)行完畢后)(譯者注:通過翻看源碼,會(huì)發(fā)現(xiàn)無論flatMap()
還是concatMap()
都包裹了一層map()
操作符)。flatMap()
使用merge()
操作符,而concatMap()
使用concat()
操作符,這就意味著后者(譯者注:這里的后者指concatMap()
)遵循元素的順序,所以,請留意是否需要保持元素次序:)。(譯者注:關(guān)于:)這個(gè)表情,請將屏幕旋轉(zhuǎn)90°)
Merge operator
將多個(gè)Observable
合并成一個(gè)。
Concat operator
按順序依次連接兩個(gè)或更多的Observable
Problem solved
concatMap()
的救贖。把flatMap()
替換成concatMap()
,問題迎刃而解。你可能會(huì)問:為什么不首先閱讀文檔(歸功于RxJava的貢獻(xiàn)者),有時(shí)候我們真的很懶,不到萬不得已絕不會(huì)去查閱文檔。這張圖是經(jīng)過測試后的最終結(jié)果(可以在最下面找到示例代碼):
參考文獻(xiàn)
希望我的片面之詞能夠?qū)δ阌兴鶐椭蝗缂韧膶⑹纠a和其他一些值得讀的資料羅列在這里。
- 源碼: https://github.com/android10/Android-ReactiveProgramming
- Functional Reactive Programming on Android With RxJava
- Grokking RxJava
- Top 7 Tips for RxJava on Android
- Mastering Observables
- React Conference London
如果有更好的辦法或者其他問題,歡迎任何形式的反饋(譯者注:當(dāng)然,小鄧子也歡迎任何形式的反饋)。