寫給小白的RxJava教程(四)

簡介:大三學生黨一枚!主攻Android開發,對於Web和後端均有了解。
個人語錄取乎其上,得乎其中,取乎其中,得乎其下,以頂級態度寫好一篇的博客。


這是RxJava系列的最後一篇,要想完全掌握RxJava使用,只有先掌握其重要的方法,在具體場景使用到的時候,再查詢其他的方法,節省學習成本。

一.再談操作符

1.1 zip操作符

zip操作符其實在Python語言中也有體現,組合兩個列表。同樣的,在這裏的功能是組合兩個Observable! 打個比方吧,兩條河流經過某處交匯最後形成一條河流,也就是把兩個Observable組合成一個新的Observable,我們來看一個例子:

 private void LogByZip() {
        Observable observable1  = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext(1);
                e.onNext(2);
                e.onNext(3);
            }
        });
        Observable observable2 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext(4);
                e.onNext(5);
                e.onNext(6);
            }
        });
        Observable.zip(observable1, observable2, 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 d) {

            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "onNext: "+integer);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

在這裏插入圖片描述

可見observable1observable2的發射事件,比如observable1發射的第一個事件是1,observable2發射的第一個事件是4他們兩個組合起來相加,觀察者收到的就是5!
就這麼簡單嗎?是的,但是還有幾點需要注意,zip也會帶來一些問題!

1.2 zip帶來的問題

1.2.1 如果兩個Observable發射事件的數量不同會產生什麼?

兩個Observable組合,如果第一個Observable發射五個事件,第二個Observable發射四個事件,那麼觀察者能收到幾個事件呢?對上面的代碼稍作修改

  private void LogByZip() {
        Observable observable1  = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.d("observable1", "subscribe: "+1);
                e.onNext(1);
                Log.d("observable1", "subscribe: "+2);
                e.onNext(2);
                Log.d("observable1", "subscribe: "+3);
                e.onNext(3);
                Log.d("observable1", "subscribe: "+4);
                e.onNext(4);
                Log.d("observable1", "subscribe: "+5);
                e.onNext(5);
            }
        });
        Observable observable2 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.d("observable2", "subscribe: "+4);
                e.onNext(4);
                Log.d("observable2", "subscribe: "+5);
                e.onNext(5);
                Log.d("observable2", "subscribe: "+6);
                e.onNext(6);
                Log.d("observable2", "subscribe: "+7);
                e.onNext(7);
            }
        });
        Observable.zip(observable1, observable2, 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 d) {

            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "onNext: "+integer);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

看看打印結果:
Observable1
在這裏插入圖片描述
observable2
在這裏插入圖片描述

組合後的Observable
在這裏插入圖片描述

由此可得出結論,如果兩個Observable組合,那麼觀察者接受到的事件數目以兩個Observable中發射事件較少的那個數目爲準。
在這裏插入圖片描述
此時我們可以引出另外一個問題,observable1發射完第一個事件以後,會繼續發送還是等待和Observable2發射的第一個事件結合完再發出第二個事件呢?我們來做一個實驗!讓Observable2每次發送完事件以後睡眠2s.看一下日誌是如何打印的

在這裏插入圖片描述

 Observable observable2 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.d("observable2", "subscribe: "+4);
                e.onNext(4);
                Thread.sleep(2000);
                Log.d("observable2", "subscribe: "+5);
                e.onNext(5);
                Thread.sleep(2000);
                Log.d("observable2", "subscribe: "+6);
                e.onNext(6);
                Thread.sleep(2000);
                Log.d("observable2", "subscribe: "+7);
                e.onNext(7);
                Thread.sleep(2000);
            }
        });

Observable1
在這裏插入圖片描述

Observable2

在這裏插入圖片描述
觀察者打印的日誌和Observable效果一樣,每隔2s打印一個。那實驗結果顯而易見了,Observable1不會等待和Observable2結合完再發出第二個事件,而是直接把事件一次性都發送出去!
在這裏插入圖片描述
那麼問題就又來了,如何存儲Observable1發出的所有事件呢?如果不存儲就會丟失,那我們就需要存儲好這些事件。就相當於Observable1一次性把事件都放進一個容器裏面,並滿足先進先出的數據結構,對沒錯就是隊列!用隊列把事件存儲起來。

那麼問題又又又來了!!!
請試想一下,這個容器是無限大的嘛?如果Observable1每秒發送1000個事件,observable2每秒發送一個事件,那麼Observable1中還剩餘999個未處理的,速度嚴重不對稱,會導致OOM.因爲所有的事件都是存放在隊列中的,並且這個隊列還不限制大小,那就有可能會造成OOM了!如何解決呢?

1.2.2 如何避免OOM?

爲了防止OOM,我們可以使用三種方式
1.降低過快的發射速度,可以讓Observable1發射完以後,睡眠一段時間!缺點,影響性能
2.可以在Observable1事件所在的隊列中取一部分處理,其他的丟棄,缺點,會導致事件丟失。
3.使用Flowable

還需要再補充同步異步的概念!請看下面的代碼

  private void syncRxJava(){
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                for (int i = 0; ; i++) {   //無限循環發事件                                              
                    e.onNext(i+"hello");
                }
            }
        }).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(String s) {
               Log.d(TAG,"onNext:"+s);
               Thread.sleep(2000);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

雖然在Observable中無限的發出事件,但是不會造成OOM,原因是什麼呢?
因爲他們兩個處在同一個線程中,調用 e.onNext(i+"hello");就相當於調用Observer中的onNext(),所以事件都會被及時處理。這就是同步,天然的屏障!但是如果是運行在不同的線程中,那就會造成速度不同,Observable不停的發,但是處理者處理每個時間要停頓兩秒,很明顯,處理者就會忙不過來,造成容器中存儲的事件數量快速增加,最後造成OOM

二.Flowable

FlowableObservable差不多,使用方式也差不多。

private void EasyUse() {
        Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<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 complete");
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR); //增加了一個參數

        Subscriber<Integer> downstream = new Subscriber<Integer>() {

            @Override
            public void onSubscribe(Subscription s) {
                Log.d(TAG, "onSubscribe");
                s.request(Long.MAX_VALUE);  //注意這句代碼
            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "onNext: " + integer);

            }

            @Override
            public void onError(Throwable t) {
                Log.w(TAG, "onError: ", t);
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete");
            }
        };

        upstream.subscribe(downstream);
    }

在這裏插入圖片描述

區別呢??????????
在這裏插入圖片描述

引用別人的一段話來說明Flowable的優點以及在代碼中註釋的s.request(Long.MAX_VALUE)

因爲Flowable在設計的時候採用了一種新的思路也就是響應式拉取的方式來更好的解決上下游流速不均衡的問題, 與我們之前所講的控制數量控制速度不太一樣, 這種方式用通俗易懂的話來說就好比是葉問打鬼子, 我們把上游看成小日本, 把下游當作葉問, 當調用Subscription.request(1)時, 葉問就說我要打一個! 然後小日本就拿出一個鬼子給葉問, 讓他打, 等葉問打死這個鬼子之後, 再次調用request(10), 葉問就又說我要打十個! 然後小日本又派出十個鬼子給葉問, 然後就在邊上看熱鬧, 看葉問能不能打死十個鬼子, 等葉問打死十個鬼子後再繼續要鬼子接着打…所以我們把request當做是一種能力, 當成下游處理事件的能力, 下游能處理幾個就告訴上游我要幾個, 這樣只要上游根據下游的處理能力來決定發送多少事件, 就不會造成一窩蜂的發出一堆事件來, 從而導致OOM. 這也就完美的解決之前我們所學到的兩種方式的缺陷, 過濾事件會導致事件丟失, 減速又可能導致性能損失. 而這種方式既解決了事件丟失的問題, 又解決了速度的問題, 完美 !

附上這位大神的博客地址RxJava教程

三.總結

用四篇博客,爲小白講解了RxJava的基本使用方法,其實RxJava遠不止如此,與Retrofit結合可以發揮出更好的實力。希望以後會寫一篇兩者結合的博客!

先別走,我有一個資源學習羣要推薦給你,它是白嫖黨的樂園,小白的天堂!
在這裏插入圖片描述
別再猶豫,一起來學習!
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章