Outline
[TOC]
前言
在上一節中, 我們提到了Flowable
和Backpressure
背壓, 本來這一節的確是想講這兩個東西的,可是寫到一半感覺還是差點火候,感覺時機未到, 因此,這里先來做個準備工作, 先帶大家學習zip
這個操作符, 這個操作符也是比較牛逼的東西了, 涉及到的東西也比較多, 主要是一些細節上的東西太多, 通過學習這個操作符,可以為我們下一節的Backpressure
做個鋪墊.
正題
照慣例我們還是先貼上一下比較正式的解釋吧.
Zip
通過一個函數將多個Observable發送的事件結合到一起,然后發送這些組合到一起的事件. 它按照嚴格的順序應用這個函數。它只發射與發射數據項最少的那個Observable一樣多的數據。
我們再用通俗易懂的圖片來解釋一下:
從這個圖中可以看見, 這次上游和以往不同的是, 我們有兩根水管了.
其中一根水管負責發送圓形事件
, 另外一根水管負責發送三角形事件
, 通過Zip操作符, 使得圓形事件
和三角形事件
合并為了一個矩形事件
.
下面我們再來看看分解動作:
通過分解動作我們可以看出:
- 組合的過程是
分別從
兩根水管里各取出一個事件
來進行組合, 并且一個事件只能被使用一次,
組合的順序是嚴格按照事件發送的順利
來進行的, 也就是說不會出現圓形1
事件和三角形B
事件進行合并, 也不可能出現圓形2
和三角形A
進行合并的情況. - 最終
下游收到的事件數量
是和上游中發送事件最少的那一根水管的事件數量
相同. 這個也很好理解, 因為是從每一根水管
里取一個事件來進行合并,最少的
那個肯定就最先取完
, 這個時候其他的水管盡管還有事件
, 但是已經沒有足夠的事件來組合了, 因此下游就不會收到剩余的事件了.
分析了大概的原理, 我們還是勞逸結合, 先來看看實際中的代碼怎么寫吧:
Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "emit 1");
emitter.onNext(1);
Log.d(TAG, "emit 2");
emitter.onNext(2);
Log.d(TAG, "emit 3");
emitter.onNext(3);
Log.d(TAG, "emit 4");
emitter.onNext(4);
Log.d(TAG, "emit complete1");
emitter.onComplete();
}
});
Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d(TAG, "emit A");
emitter.onNext("A");
Log.d(TAG, "emit B");
emitter.onNext("B");
Log.d(TAG, "emit C");
emitter.onNext("C");
Log.d(TAG, "emit complete2");
emitter.onComplete();
}
});
Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer integer, String s) throws Exception {
return integer + s;
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(String value) {
Log.d(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
我們分別創建了兩個上游水管, 一個發送1,2,3,4,Complete, 另一個發送A,B,C,Complete, 接著用Zip把發出的事件組合, 來看看運行結果吧:
D/TAG: onSubscribe
D/TAG: emit 1
D/TAG: emit 2
D/TAG: emit 3
D/TAG: emit 4
D/TAG: emit complete1
D/TAG: emit A
D/TAG: onNext: 1A
D/TAG: emit B
D/TAG: onNext: 2B
D/TAG: emit C
D/TAG: onNext: 3C
D/TAG: emit complete2
D/TAG: onComplete
結果似乎是對的... 但是總感覺什么地方不對勁...
哪兒不對勁呢, 為什么感覺是水管一發送完了之后, 水管二才開始發送啊? 到底是不是呢, 我們來驗證一下:
Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "emit 1");
emitter.onNext(1);
Thread.sleep(1000);
Log.d(TAG, "emit 2");
emitter.onNext(2);
Thread.sleep(1000);
Log.d(TAG, "emit 3");
emitter.onNext(3);
Thread.sleep(1000);
Log.d(TAG, "emit 4");
emitter.onNext(4);
Thread.sleep(1000);
Log.d(TAG, "emit complete1");
emitter.onComplete();
}
});
Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d(TAG, "emit A");
emitter.onNext("A");
Thread.sleep(1000);
Log.d(TAG, "emit B");
emitter.onNext("B");
Thread.sleep(1000);
Log.d(TAG, "emit C");
emitter.onNext("C");
Thread.sleep(1000);
Log.d(TAG, "emit complete2");
emitter.onComplete();
}
});
Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer integer, String s) throws Exception {
return integer + s;
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(String value) {
Log.d(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
這次我們在每發送一個事件之后加入了一秒鐘的延時, 來看看運行結果吧, 注意這是個GIF圖:
(貼心的我怕大家看不清楚, 特意調成了老年字體呢)
阿西吧, 好像真的是先發送的水管一再發送的水管二呢, 為什么會有這種情況呢? 因為我們兩根水管都是運行在同一個線程里, 同一個線程里執行代碼肯定有先后順序呀.
因此我們來稍微改一下, 不讓他們在同一個線程, 不知道怎么切換線程的, 請掉頭看前面幾節.
Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "emit 1");
emitter.onNext(1);
Thread.sleep(1000);
Log.d(TAG, "emit 2");
emitter.onNext(2);
Thread.sleep(1000);
Log.d(TAG, "emit 3");
emitter.onNext(3);
Thread.sleep(1000);
Log.d(TAG, "emit 4");
emitter.onNext(4);
Thread.sleep(1000);
Log.d(TAG, "emit complete1");
emitter.onComplete();
}
}).subscribeOn(Schedulers.io());
Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d(TAG, "emit A");
emitter.onNext("A");
Thread.sleep(1000);
Log.d(TAG, "emit B");
emitter.onNext("B");
Thread.sleep(1000);
Log.d(TAG, "emit C");
emitter.onNext("C");
Thread.sleep(1000);
Log.d(TAG, "emit complete2");
emitter.onComplete();
}
}).subscribeOn(Schedulers.io());
Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer integer, String s) throws Exception {
return integer + s;
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(String value) {
Log.d(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
好了, 這次我們讓水管都在IO線程里發送事件, 再來看看運行結果:
D/TAG: onSubscribe
D/TAG: emit A
D/TAG: emit 1
D/TAG: onNext: 1A
D/TAG: emit B
D/TAG: emit 2
D/TAG: onNext: 2B
D/TAG: emit C
D/TAG: emit 3
D/TAG: onNext: 3C
D/TAG: emit complete2
D/TAG: onComplete
GIF圖:
誒! 這下就對了嘛, 兩根水管同時開始發送, 每發送一個, Zip就組合一個, 再將組合結果發送給下游.
不對呀! 可能細心點的朋友又看出端倪了, 第一根水管明明發送了四個數據+一個Complete, 之前明明還有的, 為啥到這里沒了呢?
這是因為我們之前說了, zip發送的事件數量跟上游中發送事件最少的那一根水管的事件數量是有關的, 在這個例子里我們第二根水管只發送了三個事件然后就發送了Complete, 這個時候盡管第一根水管還有事件4
和事件Complete
沒有發送, 但是它們發不發送還有什么意義呢? 所以本著節約是美德的思想, 就干脆打斷它的狗腿, 不讓它發了.
至于前面的例子為什么會發送, 剛才不是已經說了是!在!同!一!個!線!程!里!嗎!!!!再問老子打死你!
有好事的程序員可能又要問了, 那我不發送Complete呢? 答案是顯然的, 上游會繼續發送事件, 但是下游仍然收不到那些多余的事件. 不信你可以試試.
實踐
學習了Zip的基本用法, 那么它在Android有什么用呢, 其實很多場景都可以用到Zip. 舉個例子.
比如一個界面需要展示用戶的一些信息, 而這些信息分別要從兩個服務器接口中獲取, 而只有當兩個都獲取到了之后才能進行展示, 這個時候就可以用Zip了:
首先分別定義這兩個請求接口:
public interface Api {
@GET
Observable<UserBaseInfoResponse> getUserBaseInfo(@Body UserBaseInfoRequest request);
@GET
Observable<UserExtraInfoResponse> getUserExtraInfo(@Body UserExtraInfoRequest request);
}
接著用Zip來打包請求:
Observable<UserBaseInfoResponse> observable1 =
api.getUserBaseInfo(new UserBaseInfoRequest()).subscribeOn(Schedulers.io());
Observable<UserExtraInfoResponse> observable2 =
api.getUserExtraInfo(new UserExtraInfoRequest()).subscribeOn(Schedulers.io());
Observable.zip(observable1, observable2,
new BiFunction<UserBaseInfoResponse, UserExtraInfoResponse, UserInfo>() {
@Override
public UserInfo apply(UserBaseInfoResponse baseInfo,
UserExtraInfoResponse extraInfo) throws Exception {
return new UserInfo(baseInfo, extraInfo);
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserInfo>() {
@Override
public void accept(UserInfo userInfo) throws Exception {
//do something;
}
});
好了, 本次的教程就到這里吧. 又到周末鳥, 下周見.