前言
首先要感謝 Season_zlc 的一系列RxJava2
的教程,關於上游、下游、水缸的類比,讓我對於整個RxJava2
的基本思想有了更加清晰的認識。大家有興趣的話一定要多看看,寫的通俗易懂,傳送門:給初學者的 RxJava 2.0 教程 (一) ,本文的思想都來源於它的一系列文章。
文章比較長,為了避免耽誤大家的時間,先列出需要介紹的知識點:
一、RxJava2 的基本模型
1.1 使用例項
在開始學習之前,我們先看一下最簡單的例子:
- 第一步:匯入依賴包:
dependencies {
//在build.gradle中,匯入依賴。
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
}
複製程式碼
- 第二步:使用最基本的
Observable
+Observer
的最簡單示例,這裡我們在上游傳送了四個onNext(String s)
事件之後,最後傳送了一個onComplete()
事件。
public static void classicalSample() {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("1");
observableEmitter.onNext("2");
observableEmitter.onNext("3");
observableEmitter.onNext("4");
observableEmitter.onComplete();
}
}).subscribe(new Observer<String>() {
private Disposable mDisposable;
@Override
public void onSubscribe(Disposable disposable) {
Log.d(TAG, "onSubscribe");
mDisposable = disposable;
}
@Override
public void onNext(String s) {
Log.d(TAG, "onNext=" + s);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
- 第三步:執行結果,訂閱成功之後,會依次回撥以下三步操作:
onSubscribe
;onNext
;onComplete
。
1.2 基本元素
在上面的例子中,涉及到了以下五個類:
Observable
:上游。ObservableOnSubscribe
:上游的create
方法所接收的引數。ObservableEmitter
:上游事件的傳送者。Observer
:下游的接收者。Disposable
:用於維繫上游、下游之間的聯絡。
對於整個模型,可以總結為以下幾點:
RxJava2
簡單的來說,就是一個傳送事件、接收事件的過程,我們可以將傳送事件方類比作上游,而接收事件方類比作下游。- 上游每產生一個事件,下游就能收到事件,上游對應
Observable
,而下游對應Observer
。 - 只有當上遊和下游建立連線之後,上游才會開始傳送事件,這一關係的建立是通過
subscribe
方法。
各關鍵元素的UML
圖如下:
1.3 ObservableEmitter
用於 發出事件,它可以分別發出onNext/onComplete/onError
事件:
- 上游可以傳送無限個
onNext
,下游也可以接收無限個onNext
。 - 當上遊傳送了一個
onComplete/onError
後,上游onComplete/onError
後的事件將會繼續傳送,但是下游在收到onComplete/onError
事件後不再繼續接收事件。 - 上游可以不傳送
onComplete
或者onError
事件。 - 呼叫
onError
或者onComplete
切斷了上游和下游的聯絡,在聯絡切斷後上游再傳送onError
事件就會報錯,onComplete
和onError
的呼叫情況有以下幾種: (1)onComplete
可以傳送多次,但是隻會收到一次回撥。 (2)onError
只可以傳送一次,傳送多次會報錯。 (3)onComplete
之後不可以傳送onError
,否則會報錯。 (4)onError
之後可以傳送onComplete
,但是隻會收到onError
事件。 onError
的引數不允許為空。
其繼承關係如下圖所示:
1.4 Disposable
理解成為 水管的機關,當呼叫它的dispose
方法時,將會將上游和下游之間的管道切斷,從而導致 下游接收不到事件。
- 在
Observer
的onSubscribe
回撥中,會傳入一個Disposable
物件,下游可以通過該物件的dispose()
方法主動切斷和上游的聯絡,在這之後上游的observableEmitter.isDisposed()
方法將返回true
。 - 當上遊和下游的聯絡切斷之後,下游收不到包括
onComplete/onError
在內的任何事件,若此時上游再呼叫onError
方法傳送事件,那麼將會報錯。
我們來模擬一下,在下游收到2
之後,通過Disposable
來切斷上游和下游之間的聯絡:
public static void classicalSample() {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
observableEmitter.onNext("1");
observableEmitter.onNext("2");
observableEmitter.onNext("3");
observableEmitter.onNext("4");
observableEmitter.onComplete();
}
}).subscribe(new Observer<String>() {
private Disposable mDisposable;
@Override
public void onSubscribe(Disposable disposable) {
Log.d(TAG, "onSubscribe");
mDisposable = disposable;
}
@Override
public void onNext(String s) {
Log.d(TAG, "onNext=" + s);
if ("2".equals(s)) {
mDisposable.dispose();
}
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
最終的執行結果為:
1.5 Subscribe 的過載方法
通過subscribe
確定上游和下游的聯絡有以下幾種方法:
- 不帶引數
Consumer<T>
類Observer
類Action
類
對於不使用Observer
類作為形參的subscribe
函式,其實實現的功能和使用Observer
類作為引數的方法相同,只不過它們是將Observer
的四個回撥分解成形參,有引數的回撥用Consumer<T>
代替,而沒有引數的則用Action
代替。
二、執行緒切換
2.1 基本概念
- 當我們在上游建立一個
Observable
來傳送事件,那麼這個上游就預設在主執行緒傳送事件;而當我們在下游建立一個Observer
來接收事件,那麼這個下游就預設在主執行緒中接收事件。 subscribeOn
指定的是 上游傳送事件 的執行緒,而observeOn
指定的是 下游接收事件 的執行緒。- 多次呼叫
subscribeOn
只有第一次有效,而每呼叫一次observeOn
,那麼下游接收訊息的執行緒就會切換一次。 CompositeDisposable
可以用來容納Disposable
物件,每當我們得到一個Disposable
物件時,就通過add
方法將它新增進入容器,在退出的時候,呼叫clear
方法,即可切斷所有的水管。
2.2 執行緒型別
Schedulers.io()
:代表IO
操作,通常用於網路請求、檔案讀寫等IO
密集型的操作。Schedulers.computation()
:代表CPU
密集型的操作,適用於大量計算。Schedulers.newThread()
:建立新的常規執行緒。AndroidSchedulers.mainThread()
:代表Android
的主執行緒。
2.3 示例
在鏈式呼叫當中,我們可以通過observeOn
方法多次切換管道下游處理訊息的執行緒,例如下面的程式碼,我們對下游進行了兩次執行緒的切換:
static void mapSample() {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
Log.d(TAG, "observableEmitter's thread=" + Thread.currentThread().getId() + ",string=true");
observableEmitter.onNext("true");
Log.d(TAG, "observableEmitter's thread=" + Thread.currentThread().getId() + ",string=false");
observableEmitter.onNext("false");
Log.d(TAG, "observableEmitter's thread=" + Thread.currentThread().getId() + ",onComplete");
observableEmitter.onComplete();
}
//1.指定了subscribe方法執行的執行緒,並進行第一次下游執行緒的切換,將其切換到新的子執行緒。
}).subscribeOn(Schedulers.io()).observeOn(Schedulers.newThread()).map(new Function<String, Boolean>() {
@Override
public Boolean apply(String s) throws Exception {
Log.d(TAG, "apply's thread=" + Thread.currentThread().getId() + ",s=" + s);
return "true".equals(s);
}
//2.進行第二次下游執行緒的切換,將其切換到主執行緒。
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(Boolean aBoolean) {
Log.d(TAG, "Observer's thread=" + Thread.currentThread().getId() + ",boolean=" + aBoolean);
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onComplete() {
Log.d(TAG, "Observer's thread=" + Thread.currentThread().getId() + ",onComplete");
}
});
}
複製程式碼
以上程式碼的執行的結果為:
三、Map 和 FlatMap 操作符
3.1 Map
Map
操作符的作用是對上游傳送的每一個事件應用一個函式,使得每個事件按照函式的邏輯進行變換,通過Map
就可以把上游傳送的每一個事件,轉換成Object
或者集合,其英文註釋為:- 以下面使用
map
的程式碼為例,可以看到map
接收一個Function
類,它有兩個泛型變數,分別為呼叫map
方法的Observable<T>
的<T>
泛型,和返回的Obervable<R>
的<R>
泛型。
public static void mapVerify() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
}
});
Observable<String> convertObservable = sourceObservable.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return integer.toString();
}
});
Log.d(TAG, "sourceObservable=" + sourceObservable + "\n convertObservable=" + convertObservable);
}
複製程式碼
Function
為一個介面:
map
函式呼叫完畢之後,將返回一個新的Observable
,它的型別為ObservableMap
:
3.2 FlatMap
FlatMap
用於將一個傳送事件的上游Observable
變換成多個傳送事件的Observable
,然後將它們傳送的事件合併,放進一個單獨的Observable
中,其註釋為:- 上游每傳送一個事件,就會針對該事件建立一個單獨的水管,然後傳送轉換後的新的事件,下游接收到的就是這些新的水管傳送的事件。
FlatMap
不保證不同水管之間事件的順序,如果需要保證順序,則需要使用contactMap
。
3.2.1 示例
static void flatMapSample() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
observableEmitter.onNext(1);
observableEmitter.onNext(2);
observableEmitter.onNext(3);
}
});
Observable<String> flatObservable = sourceObservable.flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
return Observable.fromArray("a value of " + integer + ",b value of " + integer);
}
});
flatObservable.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
}
複製程式碼
和map
操作符類似,它也接收一個型別為Function
的介面,只不過它的? extends R
引數型別換成了? extends Observable<? extends R>
。
3.2.2 FlatMap 不保證下游接收事件的順序
前面我們說到,flatMap
操作符不會保證下游接收事件的順序,下面,我們就以一個例子來說明,在flatMap
的apply
函式中,我們將一個事件轉換成兩個Observable
,並且加上了延時:
static void flatMapOrderSample() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "flatMapOrderSample emit 1");
observableEmitter.onNext(1);
Log.d(TAG, "flatMapOrderSample emit 2");
observableEmitter.onNext(2);
Log.d(TAG, "flatMapOrderSample emit 3");
observableEmitter.onNext(3);
}
});
Observable<String> flatObservable = sourceObservable.flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
Log.d(TAG, "flatMapOrderSample apply=" + integer);
long delay = (3 - integer) * 100;
return Observable.fromArray("a value of " + integer, "b value of " + integer).delay(delay, TimeUnit.MILLISECONDS);
}
});
flatObservable.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
}
複製程式碼
可以看到,最終的輸出結果和flatMap
收到事件的順序並不相同:
flatMap
換成contactMap
:
static void contactMapOrderSample() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "contactMapOrderSample emit 1");
observableEmitter.onNext(1);
Log.d(TAG, "contactMapOrderSample emit 1");
observableEmitter.onNext(2);
Log.d(TAG, "contactMapOrderSample emit 1");
observableEmitter.onNext(3);
}
});
Observable<String> flatObservable = sourceObservable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).concatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
Log.d(TAG, "contactMapOrderSample apply=" + integer);
long delay = (3 - integer) * 100;
return Observable.fromArray("a value of " + integer, "b value of " + integer).delay(delay, TimeUnit.MILLISECONDS);
}
});
flatObservable.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
}
複製程式碼
最終的執行結果為:
四、Zip 操作符
4.1 基本概念
Zip
通過一個函式從多個Observable
每次各取出一個事件,合併成一個新的事件傳送給下游。- 組合的順序是嚴格按照事件傳送的順序來的。
- 最終下游收到的事件數量和上游中傳送事件最少的那一根水管的事件數量相同。
4.1.1 兩個 Observable 執行在同一執行緒當中
static void zipSample() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "sourceObservable emit 1");
observableEmitter.onNext(1);
Thread.sleep(1000);
Log.d(TAG, "sourceObservable emit 2");
observableEmitter.onNext(2);
Log.d(TAG, "sourceObservable emit 3");
observableEmitter.onNext(3);
Log.d(TAG, "sourceObservable emit 4");
observableEmitter.onNext(4);
}
});
Observable<Integer> otherObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "otherObservable emit 1");
observableEmitter.onNext(1);
Log.d(TAG, "otherObservable emit 2");
observableEmitter.onNext(2);
Log.d(TAG, "otherObservable emit 3");
observableEmitter.onNext(3);
}
});
Observable.zip(sourceObservable, otherObservable, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) throws Exception {
return integer + integer2;
}
}).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable disposable) {
Log.d(TAG, "resultObservable onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "resultObservable onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "resultObservable onError");
}
@Override
public void onComplete() {
Log.d(TAG, "resultObservable onComplete");
}
});
}
複製程式碼
此時的執行結果為:
4.1.2 兩個 Observable 執行在不同的執行緒
static void zipSample() {
Observable<Integer> sourceObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "sourceObservable emit 1");
observableEmitter.onNext(1);
Thread.sleep(1000);
Log.d(TAG, "sourceObservable emit 2");
observableEmitter.onNext(2);
Log.d(TAG, "sourceObservable emit 3");
observableEmitter.onNext(3);
Log.d(TAG, "sourceObservable emit 4");
observableEmitter.onNext(4);
}
});
Observable<Integer> otherObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
Log.d(TAG, "otherObservable emit 1");
observableEmitter.onNext(1);
Log.d(TAG, "otherObservable emit 2");
observableEmitter.onNext(2);
Log.d(TAG, "otherObservable emit 3");
observableEmitter.onNext(3);
}
}).subscribeOn(Schedulers.io());
Observable.zip(sourceObservable, otherObservable, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) throws Exception {
return integer + integer2;
}
}).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable disposable) {
Log.d(TAG, "resultObservable onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "resultObservable onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "resultObservable onError");
}
@Override
public void onComplete() {
Log.d(TAG, "resultObservable onComplete");
}
});
}
複製程式碼
執行結果為:
五、背壓
“背壓”其實就是一種用於解決問題的工具,那麼我們的問題又是什麼呢?
- 問題:當上遊傳送事件的速度很快,下游消費事件的速度又很慢,而系統又必須快取這些上游傳送的訊息以便下游處理,那麼就會導致系統中堆積了很多的資源。
- 工具:下游告知上游目前自己的處理能力,上游根據下游的處理能力,進行適當的調整。
想必大家在很多文章中都聽過這個一句話:在RxJava2
中,Observable
不支援“背壓”,而Flowable
支援背壓。
5.1 不支援背壓的 Observable
關於Observable
不支援背壓,我們應當從兩種情況去考慮,即上游、下游是否位於相同的執行緒。
5.1.1 Observable 之上游、下游位於相同執行緒
首先,我們不呼叫observeOn
和subscribeOn
方法來改變上游、下游的工作執行緒,這樣,上游和下游就位於同一執行緒,同時,我們在下游的處理函式中,每收到一個訊息就休眠2000ms
,以模擬上游處理速度大於下游的場景。
static void oomSample() {
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
for (int i = 0; i < 1000; i++) {
Log.d(TAG, "observableEmitter=" + i);
observableEmitter.onNext(i);
}
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Thread.sleep(2000);
Log.d(TAG, "accept=" + integer);
}
});
}
複製程式碼
從下面的列印結果可以看到,當“使用 Observable
,並且上游、下游位於相同執行緒”時,並不會出現訊息堆積的情況,因為上游發射完一條訊息後,必須要等到下游處理完該訊息,才會發射一條新的訊息。
5.1.2 Observable 之上游、下游位於不同執行緒
接著,我們採用subscribeOn
和observeOn
來使得上游和下游位於不同的工作執行緒,其它均和2.2
中相同。
static void oomSample() {
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
for (int i = 0; i < 1000; i++) {
Log.d(TAG, "observableEmitter=" + i);
observableEmitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Thread.sleep(2000);
Log.d(TAG, "accept=" + integer);
}
});
}
複製程式碼
和2.2
中不同,當上遊和下游位於不同的工作執行緒,那麼上游傳送訊息時,不會考慮下游是否已經處理了之前的訊息,它會直接傳送,而這些傳送的訊息被存放在水缸當中,下游每處理完一條訊息,就去水缸中取下一條資料,那麼隨著水缸中資料越來越多,那麼系統中的無用資源就會急劇增加。
5.1.3 關於 Observable 不支援背壓的小結
我們之所以說Observable
不支援“背壓”,就是在2.1
介紹的整個族譜中,沒有一個類,一種方法能讓下游通知上游說:不要再發訊息到水缸裡了,我已經處理不過來了!
那是不是說Flowable
支援“背壓”,而Observable
不支援,那麼Observable
就要被取代了呢,其實不然,Flowable
對於“背壓”的支援是以效能為代價的,我們應當只在有可能出現2.3
中上游下游速率不匹配的問題時,才去使用Flowable
,否則就應當使用Observable
,也就是滿足兩點條件:
- 上游和下游位於不同的工作執行緒
- 上游傳送訊息的速度,要遠遠大於下游處理訊息的速度,有可能造成訊息的堆積。
5.2 支援背壓的 Flowable
5.2.1 基本概念
Flowable
和Subscriber
分別對應於之前討論的Observable
和Observer
,它們直接的連線仍然是通過subscribe
方法。Flowable
在設計的時候採用了 響應式拉取 的思想,當下遊呼叫了Subscription
的request
方法時,就表明了下游處理事件的能力,這樣上游就可以根據這個值來控制事件傳送的頻率,避免出現前面談到的上游傳送太快,而下游處理太慢從而導致OOM
的發生。- 只有上游根據下游的處理能力來傳送事件,才能達到理想的效果。
5.2.2 基本使用
static void flowSample() {
Flowable<Integer> sourceFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR);
sourceFlow.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
Log.d(TAG, "onSubscribe");
subscription.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
其類結構圖和Observable
幾乎完全一致:
5.3 Flowable 支援背壓的策略
從上面的類圖可以看出,Flowable
和Observable
最大的不同,就是在create
方法中,需要傳入額外的引數,它表示的是“背壓”的策略,這裡可選的值包括:
ERROR
BUFFER
DROP
LATEST
5.3.1 使用 ERROR 的策略
- 當上遊和下游位於同一個執行緒時,如果上游傳送的事件超過了下游宣告的
request(n)
的值,那麼會丟擲MissingBackpressureException
異常。 - 當上遊和下游位於不同執行緒時,如果上游傳送的事件超過了下游的宣告,事件會被放在水缸當中,這個水缸預設的大小是
128
,只有當下遊呼叫request
時,才從水缸中取出事件傳送給下游,如果水缸中事件的個數超過了128
,那麼也會丟擲MissingBackpressureException
異常。
下面這段程式碼,我們先將三個事件放入到水缸當中,之後每次呼叫request
方法就會從水缸當中取出一個事件傳送給下游。
static void flowSample() {
Flowable<Integer> sourceFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io());
sourceFlow.observeOn(Schedulers.newThread()).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
Log.d(TAG, "onSubscribe");
sSubscription = subscription;
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
static void clickSubscription() {
if (sSubscription != null) {
sSubscription.request(1);
}
}
複製程式碼
當上遊和下游位於不同的執行緒,每次通過Subscription
呼叫request
就會從水缸中取出一個事件,傳送給下游:
5.3.2 BUFFER 策略
- 使用
BUFFER
策略時,相當於在上游放置了一個容量無限大的水缸,所有下游暫時無法處理的訊息都放在水缸當中,這裡不再像ERROR
策略一樣,區分上游和下游是否位於同一執行緒。 - 因此,如果下游一直沒有處理訊息,那麼將會導致記憶體一直增長,從而引起
OOM
。
static void clickSubscription() {
if (sSubscription != null) {
sSubscription.request(10);
}
}
static void flowBufferSample() {
Flowable<Integer> sourceFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 10000;i ++) {
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io());
sourceFlow.observeOn(Schedulers.newThread()).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
Log.d(TAG, "onSubscribe");
sSubscription = subscription;
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
在上面的例子中,我們先把10000
條訊息放入到水缸當中,之後通過Subscription
每次從水缸中取出10
條訊息傳送給下游,演示結果為:
5.3.3 DROP 策略
- 使用
DROP
策略時,會把水缸無法存放的事件丟棄掉,這裡同樣不會受到下游和下游是否處於同一個執行緒的限制。
static void flowDropSample() {
Flowable<Integer> sourceFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 130; i++) {
emitter.onNext(i);
}
}
}, BackpressureStrategy.DROP).subscribeOn(Schedulers.io());
sourceFlow.observeOn(Schedulers.io()).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
Log.d(TAG, "onSubscribe");
sSubscription = subscription;
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
我們先往水缸中放入130
條訊息,之後每次通過Subscription
取出60
條訊息傳送給下游,可以看到,最後最多隻取到了第128
條訊息,第129/130
條訊息被丟棄了。
5.3.4 LATEST 策略
- 和
DROP
類似,當水缸無法容納下訊息時,會將它丟棄,但是除此之外,上游還會快取最新的一條訊息,例項如下:
static void flowLatestSample() {
Flowable<Integer> sourceFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 130; i++) {
emitter.onNext(i);
}
}
}, BackpressureStrategy.LATEST).subscribeOn(Schedulers.io());
sourceFlow.observeOn(Schedulers.io()).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
Log.d(TAG, "onSubscribe");
sSubscription = subscription;
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext=" + integer);
}
@Override
public void onError(Throwable throwable) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
}
複製程式碼
從下面的執行結果可以看出,當取出最後一批資料的時候,上游除了收到儲存在水缸當中的資料,還額外收到了最後一條訊息,也就是第130
條資料,這就是DROP
策略和LATEST
策略的區別:
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/