RxJava 二:RxJava 3 觀察者模式(原理解析)

目錄

1.RxJava 3

2.觀察者模式組合

3.Flowable(數據源類一)

3.1.Flowable

3.2.BackpressureStrategy背壓策略

3.3.響應式拉取(Flowable特有)

3.4.FlowableEmitter(數據發送器)

3.5.背壓問題解決方案:(Flowable按需發送數據)

4.Observable(數據源類二)

4.1.Observable

4.2.ConnectableObservable

4.3.refObservable

4.4.autoObservable

5.Observer and Subscriber


1.RxJava 3

JavaDoc

http://reactivex.io/RxJava/3.x/javadoc/

https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

添加依賴

implementation 'io.reactivex.rxjava3:rxjava:3.x.y'

implementation 'io.reactivex.rxjava3:rxandroid:3.x.y'

Java 8(來源於官方文檔)

由於Android運行時支持方面的落後,RxJava 長期基於Java 6 API。未來的 Android Studio 4中,一個叫做 desuging 的過程能夠將許多Java 7和8的特性,透明地轉換成與 Java 6兼容的特性。

So,RxJava3的baseline可以提高到 Java 8,並增加Java 8的官方支持,比如:Optional、Stream等,因此必須將項目的編譯目標設置更改爲 java8:

android {

    compileOptions {
        sourceCompatibility JavaVersion.
VERSION_1_8
       
targetCompatibility JavaVersion.VERSION_1_8
   
}

}

RxJava 3.0遵循Reactive-Streams specification規範。

2.觀察者模式組合

在RxJava 3中,提供了五對觀察者模式組合,每一對組合依靠其可調用的一系列函數的差異,具有各自的特點。

第一組:Observable(ObservableSource)/ Observer

一次可發送單條數據或者數據序列onNext,可發送完成通知onComplete或異常通知onError,不支持背壓。

第二組:Publisher(Flowable)/ Subscriber(FlowableSubscriber)

第一組基礎上進行改進,發送0到N個的數據(onNext),支持Reactive-Streams和背壓,可發送完成通知onComplete或異常通知onError,但效率沒有第一組高。

第三組:Single(SingleSource) / SingleObserver

第一組簡化版,只能發送單條數據onSuccess,或者異常通知onError

第四組:Completable(CompletableSource)/ CompletableObserver

第一組簡化版,不能發送數據,只發送完成通知onComplete或者異常通知onError

第五組:Maybe(MaybeSource) / MaybeObserver

第三,第四組的合併版,只能發送單條數據onSuccess和完成通知onComplete或者發送一條異常通知onError

3Flowable(數據源類一)

3.1.Flowable

Flowable是RxJava2.x中新增的,專門用於應對背壓(Backpressure)問題,在Observable的基礎上優化後的產物。

Observable ——> subscribe() ——> Observer

Flowable ——> subscribe() ——> Subscriber

後者是前者針對背壓問題的優化產物,前者運行效率更高,後者支持背壓問題處理,但邏輯更多,運行效率稍低,速度稍慢。

Flowable數據流程:Flowable ——> 異步緩存池(128)——> Subscriber

Flowable默認異步緩存隊列大小爲128。作用是緩存當前未來得及處理(Subscriber未接收,或者由於某種原因,Flowable未實際發送給Subscriber)的數據。也就是說隨着Subscriber對數據的接收處理,緩存池也會隨之做清理更新:去掉已經被Subscriber接收的,塞進Flowable新發送的。

注意:並不是Subscriber接收一條,便清理一條,而是存在一個延遲,等累積一段時間後統一清理一次。

所以:當生產者和消費者在相同的線程中;或者當生產者和消費者在不同線程中,但消費者消費數據的速度不低於生產者發送數據的速度,或者整條數據流中只有一條數據時 …… 使用Observable會更好。

Flowable

        .create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {
                System.out.println("發送----> 1");
                e.onNext(1);
                System.out.println("發送----> 2");
                e.onNext(2);
                System.out.println("發送----> 3");
                e.onNext(3);
                System.out.println("發送----> 完成");
                e.onComplete();
            }
        }, BackpressureStrategy.BUFFER) //指定背壓處理策略:緩存
        .subscribeOn(Schedulers.newThread())
        .observeOn(Schedulers.newThread())
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                s.request(Long.MAX_VALUE); // 響應式拉取
            }
            @Override
            public void onNext(Integer integer) {
                System.out.println("接收----> " + integer);
            }
            @Override
            public void onError(Throwable t) {}
            @Override
            public void onComplete() {
                System.out.println("接收----> 完成");
            }
        });

注意:

(1)BackpressureStrategy.BUFFER設置背壓處理策略

(2)Subscription區別於Observer的Disposable

(3)發送器FlowableEmitter,不同於Observable的ObservableEmitter

3.2.BackpressureStrategy背壓策略

Flowable本身適用於異步場景,內置默認上限128的異步緩存池,用於緩存當前未來得及處理(Subscriber未接收或者由於某種原因Flowable未實際發送給Subscriber)的數據。BackpressureStrategy的作用便是用來設置Flowable通過異步緩存池緩存數據的策略。

RxJava 3針對不同的五種策略(MISSING,ERROR,DROP,LATEST,BUFFER)內置了相應的數據發送器類,系統通過代理模式對五種數據發送器做了包裝(FlowableCreate.java中實現)。

public void subscribeActual(Subscriber<? super T> t) {

    BaseEmitter<T> emitter;
    switch (backpressure) {
        case MISSING: {
            emitter = new MissingEmitter<T>(t);
            break;
        }
        case ERROR: {
            emitter = new ErrorAsyncEmitter<T>(t);
            break;
        }
        case DROP: {
            emitter = new DropAsyncEmitter<T>(t);
            break;
        }
        case LATEST: {
            emitter = new LatestAsyncEmitter<T>(t);
            break;
        }
        default: {
            emitter = new BufferAsyncEmitter<T>(t, bufferSize());
            break;
        }
    }
    t.onSubscribe(emitter);
    // ......
}

MISSING(MissingEmitter<T>)

策略:無特定背壓處理策略,對數據無緩存和丟棄的處理,需結合創建背壓操作符確定背壓策略。

背壓操作符:

onBackpressureBuffer() 對應BackpressureStrategy.BUFFER

onBackpressureDrop() 對應BackpressureStrategy.DROP

onBackpressureLatest() 對應BackpressureStrategy.LATEST

static final class MissingEmitter<T> extends FlowableCreate.BaseEmitter<T> {

    // ......
    @Override
    public void onNext(T t) {
        if (isCancelled()) {
            return;
        }
        if (t != null) {
            actual.onNext(t);
        } else {
            onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
            return;
        }
        for (; ; ) {
            long r = get();
            if (r == 0L || compareAndSet(r, r - 1)) {
                return;
            }
        }
    }
}

ERROR(ErrorAsyncEmitter<T>)

策略:Flowable異步緩存池超限(onOverflow()),直接扔異常MissingBackpressureException,緩存池上限128。

static final class ErrorAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {

    private static final long serialVersionUID = 338953216916120960L;
    ErrorAsyncEmitter(Subscriber<? super T> actual) {
        super(actual);
    }
    @Override
    void onOverflow() {
        onError(new MissingBackpressureException("create: could not emit value due to lack of requests"));
    }
}

DROP(DropAsyncEmitter<T>)

策略:Flowable的異步緩存池滿了,直接丟掉後續發送的數據。

static final class DropAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {

    private static final long serialVersionUID = 8360058422307496563L;
    DropAsyncEmitter(Subscriber<? super T> actual) {
        super(actual);
    }
    @Override
    void onOverflow() {// nothing to do}
}

onOverflow()方法 do nothing!很明顯處理邏輯在其父類NoOverflowBaseAsyncEmitter,查看代碼邏輯,發現在調用actual.onNext(t);之前做了get() != 0的判斷,意思就是說緩存超限,什麼也不做,不超限才發送數據。也就是說如果Flowable的異步緩存池滿了,會丟掉後續由上層發送的數據(FlowableEmitter<Integer> e onNext() 發送了,但是底層actual.onNext(t);實際並未調用)

@Override

public final void onNext(T t) {
    if (isCancelled()) {
        return;
    }
    if (t == null) {
        onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
        return;
    }
    if (get() != 0) {
        actual.onNext(t);
        BackpressureHelper.produced(this, 1);
    } else {
        onOverflow();
    }
}

LATEST(LatestAsyncEmitter<T>)

策略:Flowable的異步緩存池滿了,直接丟掉後續發送的數據。但會保證最後一條數據強制緩存,並保證發送,通過內置final AtomicReference<T> queue; 實現。也就是說無論如何,Subscriber都會接收到Flowable發送的最後一條數據。

代碼比較複雜,分析略!

BUFFER(BufferAsyncEmitter<T>)

策略:通過內置

final SpscLinkedArrayQueue<T> queue;

緩存所有超限數據,該策略不會拋出MissingBackpressureException異常,不會丟失數據(在內置緩存大小範圍內),但會導致OOM。

SpscLinkedArrayQueue內部實現爲數組,大小默認128,當緩存數超過128,SpscLinkedArrayQueue會重新分配緩存空間,簡而言之,這是一個無限大的緩存池。

static final class BufferAsyncEmitter<T> extends BaseEmitter<T> {

    // ......
    final SpscLinkedArrayQueue<T> queue;
    BufferAsyncEmitter(Subscriber<? super T> actual, int capacityHint) {
        super(actual);
        this.queue = new SpscLinkedArrayQueue<T>(capacityHint);
        this.wip = new AtomicInteger();
    }
    @Override
    public void onNext(T t) {
        // ......
        queue.offer(t);
        // ......
    }
    // ......
}

3.3.響應式拉取(Flowable特有)

Subscription接口定義了兩個方法:

void cancel();  取消Subscriber與Flowable的訂閱關係。

void request(long n);  聲明並控制訂閱者對Publisher數據的需求量。

Flowable在設計的時候,採用了一種新的思路——響應式拉取方式,來設置訂閱者對數據的請求數量,Flowable可以根據訂閱者的需求量,按需發送數據。

如果不顯式調用Subscription.request(Long)則默認訂閱者的需求量爲零,所以如果Subscriber不顯示調用request方法,Flowable發送的數據並不會交給Subscriber處理。

Subscription.request(Long)可多次調用,Subscriber總共獲取的數據量是多次調用累計之和(不大於Flowable發出的數據量),如果Subscriber請求的數據量小於Flowable發送的數據量,那麼需求之外的數據仍然放到了異步緩存池中。如果異步緩存池超限,會導致MissingBackpressureException異常。

Subscription.request(0)時,報錯:java.lang.IllegalArgumentException: n > 0 required but it was 0

3.4.FlowableEmitter(數據發送器)

響應式拉取的初衷,是爲了設置訂閱者對數據的請求數量,Flowable可以根據訂閱者的需求量,按需發送數據。

FlowableEmitter可以實現這一需求,FlowableEmitter內置long requested();可以獲取當前Flowable未完成的請求數量。隨着Flowable不停發送數據,long requested();值會相應變化。

public interface FlowableEmitter<T> extends Emitter<T> {

    void setDisposable(@Nullable Disposable s);
    void setCancellable(@Nullable Cancellable c);
    long requested();
    boolean isCancelled();
    @NonNull
    FlowableEmitter<T> serialize();
    @Experimental
    boolean tryOnError(@NonNull Throwable t);
}

重點關注long requested();方法!

同步狀態下:

FlowableEmitter.requested();方法獲取到的最大未完成請求數 == Subscriber 中Subscription.request(Long)中請求的數量,與Flowable緩存隊列大小無關。

FlowableEmitter.requested();的實時值 = Subscription.request(Long)中請求的數量 – Flowable已經發送的數量。

異步狀態下:

FlowableEmitter.requested();方法獲取到的最大未完成請求數 == 128;等於Flowable緩存隊列大小,與Subscriber 中Subscription.request(Long)中請求多少無關。

Flowable有一個異步緩存池,Flowable發送的數據,先放到異步緩存池中,再由異步緩存池交給Subscriber。所以Flowable在發送數據時,首先需要考慮的不是Subscriber的數據請求量,而是緩存池中能不能放得下,放得下直接塞入,放不下執行背壓策略。至於是否超出了Subscriber的數據需求量,可以在緩存池向其傳遞數據時,再作判斷,如果未超出,則將緩存池中的數據傳遞,如果超出了,則不傳遞。

FlowableEmitter.requested();的實時值 = Flowable異步緩存池當前可用空間大小。並不是下游真正的數據請求數量!

隨着緩存池把數據發送給Subscriber,Flowable將執行清理緩存的策略,異步緩存池中的數據並不是向Subscriber發送一條便清理一條,而是每等累積到N條時,清理一次。通過e.requested()獲取到的值,正是在異步緩存池清理數據時回升的。也就是異步緩存池每次清理後,都會導致Flowable未完成請求數量的回升,理想狀態下,緩存池在Flowable發送數據時一直有空間,這樣既不會引發背壓異常,也不會導致數據遺失。

例如:

1.Flowable有N條數據(N ≤ 128),Subscription.request(M)(M < N)

Flowable實際發送N條數據到異步緩存池,再由異步緩存池發送M條數據給Subscriber。

2.Flowable有N條數據(N ≤ 128),Subscription.request(M)(M ≥ N)

Flowable實際發送N條數據到異步緩存池,再由異步緩存池發送N條數據給Subscriber。

3.Flowable有N條數據(N > 128),Subscription.request(M)(M ≤ 128)

Flowable實際發送K(N ≥ K ≥ 128,K的具體數量和Subscriber接收處理數據的速度有關)條數據到異步緩存池,其餘部分執行背壓策略,再由異步緩存池發送M條數據給Subscriber。

4.Flowable有N條數據(N > 128),Subscription.request(M)(M > 128)

Flowable實際發送K(N ≥ K ≥ 128,K的具體數量和Subscriber接收處理數據的速度有關)條數據到異步緩存池,其餘部分執行背壓策略,再由異步緩存池發送M(M < K) / K(M ≥ K)條數據給Subscriber。

注意:onComplete(),onError()並不減少requested值。

3.5.背壓問題解決方案:(Flowable按需發送數據)

如果由於訂閱者處理數據緩慢,導致Flowable異步緩存池被塞滿且無法得到清理,通過requested()獲取的值也就變成了0,會根據BackpressureStrategy背壓策略的不同,拋出MissingBackpressureException異常,或做額外緩存或者丟掉這條數據。所以Flowable只需要在e.requested()等於0時,暫停發送數據,便可解決背壓問題!!!

完整方案:

發送方按需發送;

@Override

public void subscribe(FlowableEmitter<Integer> e) throws Exception {

    int i = 0;
    while (true) {
        if (e.requested() == 0) continue; //此處添加代碼,讓flowable按需發送數據
        System.out.println("發送---->" + i);
        i++;
        e.onNext(i);
    }
}

接收方處理一條再請求下一條;

private Subscription mSubscription;

@Override
public void onSubscribe(Subscription s) {
    s.request(1);            // 設置初始請求數據量爲1
    mSubscription = s;
}
@Override
public void onNext(Integer integer) {
    try {
        Thread.sleep(50);
        System.out.println("接收------>" + integer);
        mSubscription.request(1);// 每接收到一條數據增加一條請求量
    } catch (InterruptedException ignore) {
    }
}

4.Observable(數據源類二)

Observable抽象類Observable是接口ObservableSource的一個抽象實現,我們可以通過Observable創建一個可觀察對象發送數據流。

// 發送對應的方法

Observable.create(new ObservableOnSubscribe<String>() {
    // 默認在主線程裏執行該方法
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
        e.onNext("Hello");
        e.onNext("World");
        // 結束標識
        e.onComplete();
    }
});

ObservableEmitter:數據發送器

public interface Emitter<T> {

    void onNext(@NonNull T value);
    void onError(@NonNull Throwable error);
    void onComplete();
}

(1)onNext:用來發送數據,可多次調用,每調用一次發送一條數據

(2)onError:用來發送異常通知,只發送一次,若多次調用只發送第一條

(3)onComplete:用來發送完成通知,只發送一次,若多次調用只發送第一條

onError與onComplete互斥,兩個方法只能調用一個不能同時調用!

接口Observer中的三個方法(onNext,onError,onComplete)正好與Emitter中的三個方法相對應,分別對Emitter中對應方法的行爲作出響應。

Emitter調用onNext發送數據時,Observer會通過onNext接收數據。

Emitter調用onError發送異常通知時,Observer會通過onError接收異常通知。

Emitter調用onComplete發送完成通知時,Observer會通過onComplete接收完成通知。

Observable 在2.0中被設計成了無處理背壓能力的,避免了1.0中出現的Observable不能合理背壓,導致無法預料的MissingBackpressureException出現。

參數對象ObservableOnSubscribe會被存儲在返回的 Observable 對象中,它的作用相當於一個計劃表,當 Observable 被訂閱的時候,ObservableOnSubscribe的 subscribe () 方法會自動被調用,事件序列就會依照設定依次觸發(ObservableEmitter將會被調用2次 onNext() 和1次 onComplete())。這樣,由被觀察者調用了觀察者的回調方法,就實現了由被觀察者向觀察者的事件傳遞,即觀察者模式。

4.1.Observable

通過Observable.create、Observable.interval等創建型操作符生成的Observable。

(1)當一個訂閱者訂閱Observable(包括重複訂閱)時,Observable會重新開始發送數據給訂閱者。

(2)當多個訂閱者訂閱到同一個Observable時,訂閱者收到的數據是相互獨立的。

(3)(2)情況下,當其中一個訂閱者取消訂閱,Observable只會停止對該訂閱者的數據發送。

4.2.ConnectableObservable

ConnectableObservable通過Observable.publish();Observable.reply(int N) 轉換生成。publish():訂閱者訂閱ConnectableObservable只能收到訂閱行爲以後數據源發送的數據;replay(int N):訂閱者訂閱ConnectableObservable能收到訂閱行爲之前發送的N個數據。

(1)無論ConnectableObservable有沒有訂閱者,只要調用了ConnectableObservable的connect方法,原始Observable就開始發送數據。

(2)connect返回一個Disposable對象,調用了該對象的dispose方法,原始Observable將會停止發送數據,所有ConnectableObservable的訂閱者也無法收到數據。

(3)在調用connect返回的Disposable對象後,如果重新調用了connect方法,那麼原始Observable會重新發送數據。

(4)當多個訂閱者訂閱同一個ConnectableObservable時,它們收到的數據是相同的。

(5)當一個訂閱者取消對ConnectableObservable,不會影響其他訂閱者收到消息。

(6)多個訂閱者訂閱ConnectableObservable,真正原始屬於源並不知道訂閱者的存在,和訂閱者交互的實際是ConnectableObservable,起到一個承上啓下的中介作用。

4.3.refObservable

refObservable指的是由ConnectableObservable通過.refCount() 轉換而來的Observable;或者由Observable.share()方法轉換成的Observable。

(1)當第一個訂閱者訂閱refObservable後,原始Observable開始發送數據。

(2)之後的訂閱者訂閱到refObservable後,只能接收到訂閱之後,原始Observable 發送的數據。

(3)(2)情況下,如果一個訂閱者取消訂閱refObservable,不影響其他訂閱者接收數據;如果全部訂閱者都取消了訂閱,則原始Observable停止發送數據。

4.4.autoObservable

autoObservable由ConnectableObservable通過.autoConnect(int N)轉換而來的特殊Observable:

(1)當有N個訂閱者訂閱autoObservable後,原始Observable開始發送數據。

(2)只要原始Observable開始發送數據,即使所有的訂閱者都取消了對autoObservable的訂閱,原始Observable也不會停止發送數據。

5.Observer and Subscriber

// 創建被觀察者

Observable.just("Hello", "World")
        // 將被觀察者切換到子線程
        .subscribeOn(Schedulers.io())
        // 將觀察者切換到主線程
        .observeOn(AndroidSchedulers.mainThread())
        // 創建觀察者並訂閱
        .subscribe(new Observer<String>() {
            // Disposable 相當於RxJava1.x中的 Subscription,用於解除訂閱
            private Disposable disposable;
            @Override
            public void onSubscribe(Disposable d) {
                disposable = d;
            }
            @Override
            public void onNext(String s) {
                Log.i("JAVA", "被觀察者向觀察者發送的數據:" + s);
                if (s == "-1") {   // "-1" 時爲異常數據,解除訂閱
                    disposable.dispose();
                }
            }
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onComplete() {}
        });

訂閱做了以下幾件件事:

(1)首先調用onSubscribe()方法

(2)其次調用ObservableOnSubscribe的subscribe方法,事件發送的邏輯由這裏開始運行,之後針對性地調用Observer相應方法進行數據響應。

另外,直接訂閱或傳入消費者會產生一個新的類:Disposable,用於取消訂閱。Observable在訂閱取消之後發送的數據,Observer將不會接收。

簡化訂閱:上面Observer可以通過Consumer(Consumer就是消費者的意思,替代1.0的Action0,Action1),來消費onSubscribe,onNext,onComplete,onError等事件:

RxJava 3中沒有了一系列的Action/Func接口,取而代之的是與Java8命名類似的函數式接口。Action類似於RxJava1.x中的Action0,區別在於Action允許拋出異常;Consumer即消費者,用於接收單個值,BiConsumer則是接收兩個值,Function用於變換對象Function的泛型第一個爲接收參數的數據類型,第二個爲轉換後要發送的數據類型Predicate用於判斷。這些接口命名大多參照了Java8。

Observable.just("Hello", "World")

        .subscribe(new Consumer<String>() {
            @Override
            public void accept(@NonNull String s) throws Exception {

                                       //這裏接收數據(onNext)。
                Log.i("JAVA", "被觀察者向觀察者發送的數據:" + s);
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(@NonNull Throwable throwable) throws Exception {

                                       //這裏接收異常Throwable。
            }
        }, new Action() {
            @Override
            public void run() throws Exception {

                                       //這裏接收onComplete。
            }
        }, new Consumer<Disposable>() {
            @Override
            public void accept(@NonNull Disposable disposable) throws Exception {

                                       //這裏相當於onSubscribe。
            }
        });

 

 

 

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