關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

拉丁吳發表於2019-03-04

前言

之前寫RxJava相關文章的時候,就有人想讓我談談RxJava2.0的新特性,說實話,一開始我是拒絕的。因為在我看來,RxJava2.0雖然是版本的重大升級,但總歸還是RxJava,升級一個版本還能上天是咋的?瞭解一下它的更新文件不就好了麼?真的有必要單出一篇文章來談這個麼?

但是詳細的瞭解了RxJava2.0以及部分原始碼之後,我覺得還是有必要對RxJava2.0做一個說明,幫助大家對於RxJava有更好的認識。


鋪墊

假如你還不是很熟悉RxJava,或者對於背壓這個概念(2.0更新中會涉及到背壓的概念)很模糊,希望你也能讀一讀下面兩篇鋪墊的文章:

關於背壓的那篇文章本來是本文的一部分,因為篇幅過大,被剝離出去了,所以建議大家有時間也一併閱讀。


正文

RxJava2.0有很多的更新,一些改動甚至衝擊了我之前的文章裡的內容,這也是我想寫這篇文章的原因之一。不過想要寫這篇文章其實也是有難度的,因為相關的資料去其實是很少的,還得自己硬著頭皮上….不過俗話說得好,有困難要上,沒有困難創造困難也要上。

在這裡,我會按照我們之前關於RxJava的文章的講述順序:觀察者模式,操作符,執行緒排程,這三個方面依次看有哪些更新。


新增依賴

這個估計得放在最前面。

Android端使用RxJava需要依賴新的包名:

    //RxJava的依賴包(我使用的最新版本)
    compile `io.reactivex.rxjava2:rxjava:2.0.1`
    //RxAndroid的依賴包
    compile `io.reactivex.rxjava2:rxandroid:2.0.1`複製程式碼

觀察者模式

首先宣告,RxJava以觀察者模式為骨架,在2.0中依然如此

不過此次更新中,出現了兩種觀察者模式:

  • Observable(被觀察者)/Observer(觀察者)
  • Flowable(被觀察者)/Subscriber(觀察者)

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

RxJava2.X中,Observeable用於訂閱Observer,是不支援背壓的,而Flowable用於訂閱Subscriber,是支援背壓(Backpressure)的。

關於背壓這個概念以及它在1.0版本中的缺憾在上一篇文章中我已經介紹到了,如果你不是很清楚,我在這裡在做一個介紹:背壓是指在非同步場景中,被觀察者傳送事件速度遠快於觀察者的處理速度的情況下,一種告訴上游的被觀察者降低傳送速度的策略,在1.0中,關於背壓最大的遺憾,就是集中在Observable這個類中,導致有的Observable支援背壓,有的不支援。為了解決這種缺憾,新版本把支援背壓和不支援背壓的Observable區分開來。

Observable/Observer

Observable正常用法:

  Observable mObservable=Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                e.onNext(1);
                e.onNext(2);
                e.onComplete();
            }
        });

Observer mObserver=new Observer<Integer>() {
            //這是新加入的方法,在訂閱後傳送資料之前,
            //回首先呼叫這個方法,而Disposable可用於取消訂閱
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(Integer value) {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        };

mObservable.subscribe(mObserver);複製程式碼

這種觀察者模型是不支援背壓的。

啥叫不支援背壓呢?

當被觀察者快速傳送大量資料時,下游不會做其他處理,即使資料大量堆積,呼叫鏈也不會報MissingBackpressureException,消耗記憶體過大隻會OOM

我在測試的時候,快速傳送了100000個整形資料,下游延遲接收,結果被觀察者的資料全部傳送出去了,記憶體確實明顯增加了,遺憾的是沒有OOM。

所以,當我們使用Observable/Observer的時候,我們需要考慮的是,資料量是不是很大(官方給出以1000個事件為分界線,僅供各位參考)

Flowable/Subscriber

        Flowable.range(0,10)
        .subscribe(new Subscriber<Integer>() {
            Subscription sub;
            //當訂閱後,會首先呼叫這個方法,其實就相當於onStart(),
            //傳入的Subscription s引數可以用於請求資料或者取消訂閱
            @Override
            public void onSubscribe(Subscription s) {
                Log.w("TAG","onsubscribe start");
                sub=s;
                sub.request(1);
                Log.w("TAG","onsubscribe end");
            }

            @Override
            public void onNext(Integer o) {
                Log.w("TAG","onNext--->"+o);
                sub.request(1);
            }
            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }
            @Override
            public void onComplete() {
                Log.w("TAG","onComplete");
            }
        });複製程式碼

輸出如下:

onsubscribe start
onNext--->0
onNext--->1
onNext--->2
...
onNext--->9
onComplete
onsubscribe end複製程式碼

Flowable是支援背壓的,也就是說,一般而言,上游的被觀察者會響應下游觀察者的資料請求,下游呼叫request(n)來告訴上游傳送多少個資料。這樣避免了大量資料堆積在呼叫鏈上,使記憶體一直處於較低水平。

當然,Flowable也可以通過creat()來建立:

        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {
                e.onNext(1);
                e.onNext(2);
                e.onNext(3);
                e.onNext(4);
                e.onComplete();
            }
        }
        //需要指定背壓策略
        , BackpressureStrategy.BUFFER);複製程式碼

Flowable雖然可以通過create()來建立,但是你必須指定背壓的策略,以保證你建立的Flowable是支援背壓的(這個在1.0的時候就很難保證,可以說RxJava2.0收緊了create()的許可權)。

根據上面的程式碼的結果輸出中可以看到,當我們呼叫subscription.request(n)方法的時候,不等onSubscribe()中後面的程式碼執行,就會立刻執行到onNext方法,因此,如果你在onNext方法中使用到需要初始化的類時,應當儘量在subscription.request(n)這個方法呼叫之前做好初始化的工作;

當然,這也不是絕對的,我在測試的時候發現,通過create()自定義Flowable的時候,即使呼叫了subscription.request(n)方法,也會等onSubscribe()方法中後面的程式碼都執行完之後,才開始呼叫onNext。

TIPS: 儘可能確保在request()之前已經完成了所有的初始化工作,否則就有空指標的風險。

其他觀察者模式

當然,除了上面這兩種觀察者,還有一類觀察者

  • Single/SingleObserver
  • Completable/CompletableObserver
  • Maybe/MaybeObserver

其實這三者都差不多,Maybe/MaybeObserver可以說是前兩者的複合體,因此以Maybe/MaybeObserver為例簡單介紹一下這種觀察者模式的用法

//判斷是否登陸
Maybe.just(isLogin())
    //可能涉及到IO操作,放在子執行緒
    .subscribeOn(Schedulers.newThread())
    //取回結果傳到主執行緒
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new MaybeObserver<Boolean>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onSuccess(Boolean value) {
                if(value){
                    ...
                }else{
                    ...
                }
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });複製程式碼

上面就是Maybe/MaybeObserver的普通用法,你可以看到,實際上,這種觀察者模式並不用於傳送大量資料,而是傳送單個資料,也就是說,當你只想要某個事件的結果(true or false)的時候,你可以用這種觀察者模式


這是上面那些被觀察者的上層介面:

//Observable介面
interface ObservableSource<T> {
    void subscribe(Observer<? super T> observer);
}
//Single介面
interface SingleSource<T> {
    void subscribe(SingleObserver<? super T> observer);
}
//Completable介面
interface CompletableSource {
    void subscribe(CompletableObserver observer);
}
//Maybe介面
interface MaybeSource<T> {
    void subscribe(MaybeObserver<? super T> observer);
}
//Flowable介面
public interface Publisher<T> {
    public void subscribe(Subscriber<? super T> s);
}複製程式碼

其實我們可以看到,每一種觀察者都繼承自各自的介面,這也就把他們能完全的區分開,各自獨立(特別是Observable和Flowable),保證了他們各自的擴充或者配套的操作符不會相互影響。

例如flatMap操作符實現:

//Flowable中flatMap的定義
Flowable<R> flatMap(Function<? super T, ? extends Publisher<? extends R>> mapper);

//Observable中flatMap的定義
Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper);複製程式碼

假如你想為Flowable寫一個自定義的操作符,那麼只要保證Function< Publisher >中的型別實現了Publisher介面即可。這麼說可能很抽象,大家不理解其實也沒關係,因為並不推薦大家自定義操作符,RxJava中的操縱符的組合已經可以滿足大家的需求了。

當然,你也會注意到上面那些介面中的subscribe()方法的返回型別為void了,在1.X中,這個方法一般會返回一個Subscription物件,用於取消訂閱。現在,這個功能的物件已經被放到觀察者Observer或者subscriber的內部實現方法中了,

Flowable/Subscriber

public interface Subscriber<T> {  
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}

public interface Subscription {
    public void request(long n);
    public void cancel();
}複製程式碼

上面的例項中,onSubscribe(Subscription s)傳入的引數s就肩負著取消訂閱的功能,當然,他也可以用於請求上游的資料。

在Observable/observer中,傳入的引數是另一個物件

Observable/Observer

public interface Observer<T> {
   void onSubscribe(Disposable d);
    void onNext(T value);
    void onError(Throwable e);
    void onComplete();
}

public interface Disposable {
    /**
     * Dispose the resource, the operation should be idempotent.
     */
    void dispose();
    /**
     * Returns true if this resource has been disposed.
     * @return true if this resource has been disposed
     */
    boolean isDisposed();
}複製程式碼

在Observer介面中,onSubscribe(Disposable d)方法傳入的Disposable也是用於取消訂閱,基本功能是差不多的,只不過命名不一致,大家知道就好。

其實這種設計可以說還是符合邏輯的,因為取消訂閱這個動作就只有觀察者(Observer等)才能做的,現在把它併入到觀察者內部,也算順理成章吧。

最後再提一點更新,就是被觀察者不再接收null作為資料來源了。


操作符相關

這一塊其實可以說沒什麼改動,大部分之前你用過的操作符都沒變,即使有所變動,也只是包名或類名的改動。大家可能經常用到的就是Action和Function。

Action相關

之前我在文章裡介紹過關於Action這類介面,在1.0中,這類介面是從Action0,Action1…往後排(數字代表可接受的引數),現在做出了改動

Rx1.0———–Rx2.0

Action0——–Action
Action1——–Consumer
Action2——–BiConsumer
後面的Action都去掉了,只保留了ActionN

Function相關

同上,也是命名方式的改變

上面那兩個類,和RxJava1.0相比,他們都增加了throws Exception,也就是說,在這些方法做某些操作就不需要try-catch

例如:

Flowable.just("file.txt")
.map(name -> Files.readLines(name))
.subscribe(lines -> System.out.println(lines.size()), Throwable::printStackTrace);複製程式碼

Files.readLines(name)這類io方法本來是需要try-catch的,現在直接丟擲異常,就可以放心的使用lambda ,保證程式碼的簡潔優美。

doOnCancel/doOnDispose/unsubscribeOn

以doOnCancel為例,大概就是當取消訂閱時,會呼叫這個方法,例如:

Flowable.just(1, 2, 3)
.doOnCancel(() -> System.out.println("Cancelled!"))
.take(2)
.subscribe(System.out::println);複製程式碼

take新操符會取消後面那些還未被髮送的事件,因而會觸發doOnCancel

其他的一些操作符基本沒變,或者只是改變了名字,在這裡就不一一介紹了,需要提一下的是,很多操作符都有兩套,一套用於Observable,一套用於Flowable。


執行緒排程

可以說這一塊兒基本也沒有改動,如果一定要說的話。

  • 那就是去掉了Schedulers.immediate()這個執行緒環境
  • 移除的還有Schedulers.test()(我好像從來沒用過這個方法)
  • io.reactivex.Scheduler這個抽象類支援直接排程自定義執行緒任務(這個我也沒怎麼用)

補充

如果你想把自己的RxJava1.0的遷移到2.0的版本,可以使用這個庫RxJava2Interop,它可以在Rxjava1.0和2.0之間相互轉換,也就是說,不僅可以把1.0的程式碼遷移到2.0,你還可以把2.0的程式碼遷移到1.0,哈哈。

補充2

在RxJava1.0中,有的人會使用CompositeSubscription來收集Subscription,來統一取消訂閱,現在在RxJava2.0中,由於subscribe()方法現在返回void,那怎麼辦呢?

其實在RxJava2.0中,Flowable提供了subscribeWith這個方法可以返回當前訂閱的觀察者,並且通過ResourceSubscriber DisposableSubscriber等觀察者來提供 Disposable的介面

所以,如果想要達成RxJava1.0的效果,現在應該是這樣做:

CompositeDisposable composite = new CompositeDisposable();

composite.add(Flowable.range(1, 8).subscribeWith(subscriber));

這個subscriber 應該是 ResourceSubscriber 或者 DisposableSubscriber 的例項。


結尾

其實從整篇文章的分析來看,改動最大的還是觀察者模式的實現,被拆分和細化了,主要分成了Observable和Flowable兩大類,當然還有與之相關聯的其他變動,總體來看這一版本可以說是對於觀察者和被觀察者的重構

RxJava2.0的範例程式碼我沒精力去寫了,也正巧有位外國朋友已經寫了RxJava2.0的demo,下面是他的專案地址:

RxJava2-Android-Samples

當然,學習2.0 的過程中有什麼問題也可以在這裡留言討論。


後記

這篇文章半個月前就開始寫了,但是一直不太滿意,所以在草稿箱裡躺了很久,但是本著不放棄任何一篇落後文章的信念,還是振作起來,完成關於RxJava2.0的介紹。你可能不信,寫完頓時有了一種解(xu)脫的感覺。

身體被掏空…


附錄

下面我截圖展示一下2.0相對於1.0的一些改動的細節,僅做參考。

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

關於 RxJava 最友好的文章—— RxJava 2.0 全新來襲

其實這些都是官方給出的列表,截圖在這裡只是方便大家觀摩。

相關文章