我學 rxjava 2(3)- 熱發射

這篇文章離上一篇文章有些時日了,概因最難心情大大的不好,非常不爽。

為啥我會專門寫一下熱發射呢,因為 RxBus 就是使用 RxJava 的熱發射(Subject)實現的,但是呢我的出發點不同,我是因為研究了 AAC 的 LivaData 之后才有感而發的,同樣是響應式編程,我們如何使用 RxJava 實現構建數據和 UI 之間的通道,做到動態更新數據呢

先來回顧一下 LivaData 的使用,這是介紹 AAC 組件:Android Architecture Components 開發架構 中 demo 的數據流程圖

數據流程圖

UI 層拿到 modle層拋出的 livedata(Observable) ,在其上注冊更新 UI 的方法,然后 modle層在更新數據的時候直接調用 livedata.setValue 方法就可以更新 UI 層的數據了

這其實就是 RxJava 中熱發射的典型使用,所以我們真的有必要去學習一下了

先來概念


有熱必然有冷,那何為熱,何為冷,這點很重要的:

  • 冷發射 寫
    Observable 在收到 Observer 注冊時就發送數據,此為冷,我們平時使用的 RxJava 方式都是冷發射
  • 熱發射
    Observable 在收到 Observer 注冊時只是建立了相互關系,可以稱之為管道。之后 Observable 可以在需要的任何時候發射任意數據。

對于概念我覺得解釋的比較明白的是這篇文章里的:Learn RxJava 2 Observables 與 Subscribers

細細品鑒


冷發射經典應用如下

        Observable.just(1, 2, 3)
                .subscribe(integer -> {
                     ......
                });
  1. 冷發射的數據固定,要不是固定的數據,要不就是一個固定的從遠程 IP 獲取的數據,是相對寫死的
  2. 冷發射在注冊時即開始發射數據,我們不能決定發射數據的時機和地點

熱發射經典應用如下

        // 創建熱發射 Observable 
        PublishSubject<String> subject = PublishSubject.create();
        Disposable disposable = subject.subscribe(s -> show(s));
        // 動態發送數據
        subject.onNext("響應式編程");
        // 中斷管道,接觸注冊關系
        disposable.dispose();
  1. 熱發射我們可以決定發射時機,地點,數據,靈活可以實現和 livadata 相同的效果
  2. 可以解除單個注冊

熱發射核心 Subject


Subject 這個東西既可以當 Observable 用,上面我們已經看到了,也可以當 Observer 用,接受別的 Observable 的數據

我們先來看看 Subject 的幾個實現類,然后才好往下繼續

  • PublishSubject
    • 該Subject不會改變事件的發送順序。
    • 如果在已經發送了一部分事件之后注冊的observer,
    • 是不會收到之前發送的事件。
private void doPublishSubject() {
        //將事件發送到observer,如果先前已經漏掉的事件,不會重新發送到后注冊的observer上
        PublishSubject<String> publish = PublishSubject.create();
        publish.subscribe(new PublishObserver<String>("first"));
        publish.onNext("1");
        publish.onNext("2");
        publish.subscribe(new PublishObserver<String>("seconde"));
        publish.onNext("3");
        publish.onCompleted();
    }
  • BehaviorSubject
    • 該類有創建時需要一個默認參數,該默認參數會在subject未發送過其他的事件時,向注冊的observer發送。
    • 注意看代碼注釋
private void doBehaviorSubject() {
        //將事件發送到observer,如果先前已經漏掉的事件,除了最近的一個事件以外,
        //其他相關事件不會重新發送到后注冊的observer上。所以需要帶默認值,
        //第一次被observer注冊時,observable中沒有內容的時候,就會將默認值發給observer
        BehaviorSubject<String> behavior = BehaviorSubject.create("創建beahavior時候帶的消息");
        behavior.subscribe(new SubjectObserver<String>("first"));
        behavior.onNext("1");
        behavior.onNext("2");
        behavior.subscribe(new SubjectObserver<String>("seconde"));
        behavior.onNext("3");
        behavior.onCompleted();
    }
  • ReplaySubject
    • 將事件發送到observer,無論什么時候注冊observer,
    • 無論何時通過該observable發射的所有事件,均會發送給新的observer。
private void doReplaySubject() {
        //將事件發送到observer,無論什么時候注冊observer,
        //無論何時通過該observable發射的所有事件,均會發送給新的observer。
        ReplaySubject<String> replay = ReplaySubject.create();
        replay.subscribe(new SubjectObserver<String>("first"));
        replay.onNext("1");
        replay.onNext("2");
        replay.subscribe(new SubjectObserver<String>("seconde"));
        replay.onNext("3");
        replay.onCompleted();
    }
  • AsyncSubject
    • 只有當subject調用onComplete方法時,才會將subject中的最后一個事件傳遞給observer。
    • 如果不調用onComplete方法,則不會給observer發送任何事件。
private void doAsyncSubject() {
        //只會有當subject調用onComplete方法時,才會將subject中的最后一個事件傳遞給observer。
        //如果不調用onComplete方法,則不會向observer中發送任何事件
        AsyncSubject async = AsyncSubject.create();
        async.subscribe(new SubjectObserver<String>("first"));
        async.onNext("1");
        async.onNext("2");
        async.onNext("3");
        async.onCompleted();
        async.subscribe(new SubjectObserver<String>("seconde"));
        async.onCompleted();
    }
  • Subject 作為Observer ,注冊到一個冷發射的 Observable 上面
    • 注意此時我們只能使用 ReplaySubject
    • 因為冷發射注冊既發射數據,所有這個 Subject 在注冊到冷發射的 Observable 上時就會接受這個冷發射的 Observable 的數據,然后繼續向下傳遞
        ReplaySubject<String> subject = ReplaySubject.create();
        subject.subscribe(s -> show(s));

        Observable<String> observable = Observable.just("AA");
        observable.subscribe(subject);
  • Subject 作為Observer ,注冊到一個熱發射的 Observable 上面
    • 這是既沒有類型限制了
    • 我們添加一個變換進來,要不然沒啥意義,這個其實和 livadata 天劍 transform 一個思路
        PublishSubject<String> subject1 = PublishSubject.create();
        PublishSubject<String> subject2 = PublishSubject.create();

        subject1.map(new Function<String, String>() {
            @Override
            public String apply(String s) throws Exception {
                return s + "_經過2的修改了";
            }
        }).subscribe(subject2);

        subject2.subscribe(s -> show(s));

        subject1.onNext("響應式編程");

ConnnectableObservbale


我們可以把一個冷發射轉為熱發射,使用 publish

ConnectableObservable<String> source = Observable
                .just("Alpha","Beta","Delta","Gamma","Epsilon")
                .publish();
        source.subscribe(s -> System.out.println("observer1 RECEIVED: " + s));

        source.map(String::length)
                .subscribe(i -> System.out.println("observer2 RECEIVED: " + i));
         //發射!
        source.connect();

不過我覺得這個 ConnnectableObservbale 沒啥發用,寫死數據的熱發射沒太大應用價值。


代碼特征

PublishSubject 我想大家肯定有一些疑問這里我測試過直接上答案

1. PublishSubject 變換不會丟失通道的特性,并且一樣可以多注冊

        val subject = PublishSubject.create<String>()
        val observable = subject.map {
            return@map "BB"
        }

        observable.subscribe {
            Log.d("AA", "收到數據1:$it")
        }

        observable.subscribe {
            Log.d("AA", "收到數據2:$it")
        }

        subject.onNext("AA")

PublishSubject 變換之后雖然我們拿到的是 Observable,但是 PublishSubject 特性不會丟失,為啥?因為數據源頭不會像 Observable 一樣由注冊的就觸發數據,這個 Observable 的源頭還是 PublishSubject ,Observable 求到的只是銜接作用


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容