RxJava 知識梳理(3) RxJava2 基礎知識小結

澤毛發表於2017-12-21

前言

首先要感謝 Season_zlc 的一系列RxJava2的教程,關於上游、下游、水缸的類比,讓我對於整個RxJava2的基本思想有了更加清晰的認識。大家有興趣的話一定要多看看,寫的通俗易懂,傳送門:給初學者的 RxJava 2.0 教程 (一) ,本文的思想都來源於它的一系列文章。

文章比較長,為了避免耽誤大家的時間,先列出需要介紹的知識點:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

一、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");

            }
        });
    }
複製程式碼
  • 第三步:執行結果,訂閱成功之後,會依次回撥以下三步操作:onSubscribeonNextonComplete
    RxJava 知識梳理(3)   RxJava2 基礎知識小結

1.2 基本元素

在上面的例子中,涉及到了以下五個類:

  • Observable:上游。
  • ObservableOnSubscribe:上游的create方法所接收的引數。
  • ObservableEmitter:上游事件的傳送者。
  • Observer:下游的接收者。
  • Disposable:用於維繫上游、下游之間的聯絡。

對於整個模型,可以總結為以下幾點:

  • RxJava2簡單的來說,就是一個傳送事件、接收事件的過程,我們可以將傳送事件方類比作上游,而接收事件方類比作下游。
  • 上游每產生一個事件,下游就能收到事件,上游對應Observable,而下游對應Observer
  • 只有當上遊和下游建立連線之後,上游才會開始傳送事件,這一關係的建立是通過subscribe方法。

各關鍵元素的UML圖如下:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

1.3 ObservableEmitter

用於 發出事件,它可以分別發出onNext/onComplete/onError事件:

  • 上游可以傳送無限個onNext,下游也可以接收無限個onNext
  • 當上遊傳送了一個onComplete/onError後,上游onComplete/onError後的事件將會繼續傳送,但是下游在收到onComplete/onError事件後不再繼續接收事件。
  • 上游可以不傳送onComplete或者onError事件。
  • 呼叫onError或者onComplete切斷了上游和下游的聯絡,在聯絡切斷後上游再傳送onError事件就會報錯,onCompleteonError的呼叫情況有以下幾種: (1) onComplete可以傳送多次,但是隻會收到一次回撥。 (2) onError只可以傳送一次,傳送多次會報錯。 (3) onComplete之後不可以傳送onError,否則會報錯。 (4) onError之後可以傳送onComplete,但是隻會收到onError事件。
  • onError的引數不允許為空。

其繼承關係如下圖所示:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

1.4 Disposable

理解成為 水管的機關,當呼叫它的dispose方法時,將會將上游和下游之間的管道切斷,從而導致 下游接收不到事件

  • ObserveronSubscribe回撥中,會傳入一個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");

            }
        });
    }
複製程式碼

最終的執行結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

1.5 Subscribe 的過載方法

通過subscribe確定上游和下游的聯絡有以下幾種方法:

RxJava 知識梳理(3)   RxJava2 基礎知識小結
可以看到,這裡可以分為三類:

  • 不帶引數
  • Consumer<T>
  • Observer
  • Action

對於不使用Observer類作為形參的subscribe函式,其實實現的功能和使用Observer類作為引數的方法相同,只不過它們是將Observer的四個回撥分解成形參,有引數的回撥用Consumer<T>代替,而沒有引數的則用Action代替。

RxJava 知識梳理(3)   RxJava2 基礎知識小結

二、執行緒切換

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");
            }
        });
    }
複製程式碼

以上程式碼的執行的結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

三、Map 和 FlatMap 操作符

3.1 Map

  • Map操作符的作用是對上游傳送的每一個事件應用一個函式,使得每個事件按照函式的邏輯進行變換,通過Map就可以把上游傳送的每一個事件,轉換成Object或者集合,其英文註釋為:
    RxJava 知識梳理(3)   RxJava2 基礎知識小結
  • 以下面使用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為一個介面:

RxJava 知識梳理(3)   RxJava2 基礎知識小結
並且在map函式呼叫完畢之後,將返回一個新的Observable,它的型別為ObservableMap
RxJava 知識梳理(3)   RxJava2 基礎知識小結

3.2 FlatMap

  • FlatMap用於將一個傳送事件的上游Observable變換成多個傳送事件的Observable,然後將它們傳送的事件合併,放進一個單獨的Observable中,其註釋為:
    RxJava 知識梳理(3)   RxJava2 基礎知識小結
  • 上游每傳送一個事件,就會針對該事件建立一個單獨的水管,然後傳送轉換後的新的事件,下游接收到的就是這些新的水管傳送的事件。
  • 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操作符不會保證下游接收事件的順序,下面,我們就以一個例子來說明,在flatMapapply函式中,我們將一個事件轉換成兩個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收到事件的順序並不相同:

RxJava 知識梳理(3)   RxJava2 基礎知識小結
下面,還是同樣的場景,將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);
            }
        });
    }
複製程式碼

最終的執行結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

四、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");

            }
        });
    }
複製程式碼

此時的執行結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

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");

            }
        });
    }
複製程式碼

執行結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

五、背壓

“背壓”其實就是一種用於解決問題的工具,那麼我們的問題又是什麼呢?

  • 問題:當上遊傳送事件的速度很快,下游消費事件的速度又很慢,而系統又必須快取這些上游傳送的訊息以便下游處理,那麼就會導致系統中堆積了很多的資源。
  • 工具:下游告知上游目前自己的處理能力,上游根據下游的處理能力,進行適當的調整。

想必大家在很多文章中都聽過這個一句話:在RxJava2中,Observable不支援“背壓”,而Flowable支援背壓。

5.1 不支援背壓的 Observable

關於Observable不支援背壓,我們應當從兩種情況去考慮,即上游、下游是否位於相同的執行緒。

5.1.1 Observable 之上游、下游位於相同執行緒

首先,我們不呼叫observeOnsubscribeOn方法來改變上游、下游的工作執行緒,這樣,上游和下游就位於同一執行緒,同時,我們在下游的處理函式中,每收到一個訊息就休眠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,並且上游、下游位於相同執行緒”時,並不會出現訊息堆積的情況,因為上游發射完一條訊息後,必須要等到下游處理完該訊息,才會發射一條新的訊息。

RxJava 知識梳理(3)   RxJava2 基礎知識小結

5.1.2 Observable 之上游、下游位於不同執行緒

接著,我們採用subscribeOnobserveOn來使得上游和下游位於不同的工作執行緒,其它均和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中不同,當上遊和下游位於不同的工作執行緒,那麼上游傳送訊息時,不會考慮下游是否已經處理了之前的訊息,它會直接傳送,而這些傳送的訊息被存放在水缸當中,下游每處理完一條訊息,就去水缸中取下一條資料,那麼隨著水缸中資料越來越多,那麼系統中的無用資源就會急劇增加。

RxJava 知識梳理(3)   RxJava2 基礎知識小結

5.1.3 關於 Observable 不支援背壓的小結

我們之所以說Observable不支援“背壓”,就是在2.1介紹的整個族譜中,沒有一個類,一種方法能讓下游通知上游說:不要再發訊息到水缸裡了,我已經處理不過來了!

那是不是說Flowable支援“背壓”,而Observable不支援,那麼Observable就要被取代了呢,其實不然,Flowable對於“背壓”的支援是以效能為代價的,我們應當只在有可能出現2.3中上游下游速率不匹配的問題時,才去使用Flowable,否則就應當使用Observable,也就是滿足兩點條件:

  • 上游和下游位於不同的工作執行緒
  • 上游傳送訊息的速度,要遠遠大於下游處理訊息的速度,有可能造成訊息的堆積。

5.2 支援背壓的 Flowable

5.2.1 基本概念

  • FlowableSubscriber分別對應於之前討論的ObservableObserver,它們直接的連線仍然是通過subscribe方法。
  • Flowable在設計的時候採用了 響應式拉取 的思想,當下遊呼叫了Subscriptionrequest方法時,就表明了下游處理事件的能力,這樣上游就可以根據這個值來控制事件傳送的頻率,避免出現前面談到的上游傳送太快,而下游處理太慢從而導致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幾乎完全一致:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

5.3 Flowable 支援背壓的策略

從上面的類圖可以看出,FlowableObservable最大的不同,就是在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就會從水缸中取出一個事件,傳送給下游:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

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條訊息傳送給下游,演示結果為:

RxJava 知識梳理(3)   RxJava2 基礎知識小結

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條訊息被丟棄了。

RxJava 知識梳理(3)   RxJava2 基礎知識小結

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策略的區別:

RxJava 知識梳理(3)   RxJava2 基礎知識小結


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章