RxJava入門與提高-Subject篇(2)

前言

歡迎繼續收看《RxJava入門與提高》,上周出了第一篇 RxJava入門與提高(1)
本文主要給大家補充一下上一篇遺留的Subject知識,沒看過上一篇的同學、忘了上一章寫什么的同學、還有其他同學,RxJava入門與提高(1)。溫習一遍,俗話說,“書讀百遍,奇異自見”,看多一遍是一遍,多多益善嘛。溫習完的,請回來繼續聽講。


Subject

關于Subject,官方文檔的解釋是這樣的:Subject可以看成是一個橋梁或者代理,在某些ReactiveX實現中(如RxJava),它同時充當了Observer和Observable的角色。因為它是一個Observer,它可以訂閱一個或多個Observable;又因為它是一個Observable,它可以轉發它收到(Observe)的數據,也可以發射新的數據。從官方解釋中,我提取出三個要點:

  1. 它可以充當Observable;

  2. 它可以充當Observer;

  3. 它是Observable和Observer之間的橋梁;

接下來對這三個要點解釋一下,但在解釋之前,要先介紹一下Subject的種類, Subject是一個抽象類,不能通過new來實例化Subject,所以Subject有四個實現類,分別為AsyncSubjectBehaviorSubjectPublishSubjectReplaySubject,每個實現類都有特定的“技能”,下面結合代碼來介紹一下它們各自的“技能”。注意,所有的實現類都由create()方法實例化,無需new,所有的實現類調用onCompleted()onError(),它的Observer將不再接收數據;


Subject的分類解析

  • AsyncSubject
    Observer會接收AsyncSubject的``onComplete()`之前的最后一個數據,如果因異常而終止,AsyncSubject將不會釋放任何數據,但是會向Observer傳遞一個異常通知。示例代碼如下:

          AsyncSubject<String> asyncSubject = AsyncSubject.create();
          asyncSubject.onNext("asyncSubject1");
          asyncSubject.onNext("asyncSubject2");
          asyncSubject.onNext("asyncSubject3");  
          asyncSubject.onCompleted();
          asyncSubject.subscribe(new Observer<String>() {
              @Override
              public void onCompleted() {
    
                  LogUtil.log("asyncSubject onCompleted");  //輸出 asyncSubject onCompleted
              }
    
              @Override
              public void onError(Throwable e) {
    
                  LogUtil.log("asyncSubject onError");  //不輸出(異常才會輸出)
              }
    
              @Override
              public void onNext(String s) {
    
                  LogUtil.log("asyncSubject:"+s);  //輸出asyncSubject:asyncSubject3
              }
          });
    

    以上代碼,Observer只會接收asyncSubject的onCompleted()被調用前的最后一個數據,即“asyncSubject3”,如果不調用onCompleted(),Subscriber將不接收任何數據。

  • BehaviorSubject
    Observer會接收到BehaviorSubject被訂閱之前的最后一個數據,再接收其他發射過來的數據,如果BehaviorSubject被訂閱之前沒有發送任何數據,則會發送一個默認數據。(注意跟AsyncSubject的區別,AsyncSubject要手動調用onCompleted(),且它的Observer會接收到onCompleted()前發送的最后一個數據,之后不會再接收數據,而BehaviorSubject不需手動調用onCompleted(),它的Observer接收的是BehaviorSubject被訂閱前發送的最后一個數據,兩個的分界點不一樣,且之后還會繼續接收數據。)示例代碼如下:

      BehaviorSubject<String> behaviorSubject = BehaviorSubject.create("default");
      behaviorSubject.onNext("behaviorSubject1");
      behaviorSubject.onNext("behaviorSubject2");
          behaviorSubject.subscribe(new Observer<String>() {
              @Override
              public void onCompleted() {
    
                  LogUtil.log("behaviorSubject:complete");
              }
    
              @Override
              public void onError(Throwable e) {
    
                  LogUtil.log("behaviorSubject:error");
              }
    
              @Override
              public void onNext(String s) {
    
                  LogUtil.log("behaviorSubject:"+s);
              }
          });
    
          behaviorSubject.onNext("behaviorSubject3");
          behaviorSubject.onNext("behaviorSubject4");
    

    以上代碼,Observer會接收到behaviorSubject2、behaviorSubject3、behaviorSubject4,如果在behaviorSubject.subscribe()之前不發送behaviorSubject1、behaviorSubject2,則Observer會先接收到default,再接收behaviorSubject3、behaviorSubject4。

  • PublishSubject
    PublishSubject比較容易理解,相對比其他Subject常用,它的Observer只會接收到PublishSubject被訂閱之后發送的數據。示例代碼如下:

      PublishSubject<String> publishSubject = PublishSubject.create();
      publishSubject.onNext("publishSubject1");
      publishSubject.onNext("publishSubject2");
      publishSubject.subscribe(new Observer<String>() {
              @Override
              public void onCompleted() {
    
              }
    
              @Override
              public void onError(Throwable e) {
    
              }
    
              @Override
              public void onNext(String s) {
                  LogUtil.log("publishSubject observer1:"+s);
              }
          });
      publishSubject.onNext("publishSubject3");
      publishSubject.onNext("publishSubject4");
    

    以上代碼,Observer只會接收到"behaviorSubject3"、"behaviorSubject4"。

  • ReplaySubject
    ReplaySubject會發射所有數據給觀察者,無論它們是何時訂閱的。也有其它版本的ReplaySubject,在重放緩存增長到一定大小的時候或過了一段時間后會丟棄舊的數據。示例代碼如下:

    ReplaySubject<String>replaySubject = ReplaySubject.create(); //創建默認初始緩存容量大小為16的ReplaySubject,當數據條目超過16會重新分配內存空間,使用這種方式,不論ReplaySubject何時被訂閱,Observer都能接收到數據
    //replaySubject = ReplaySubject.create(100);//創建指定初始緩存容量大小為100的ReplaySubject
    //replaySubject = ReplaySubject.createWithSize(2);//只緩存訂閱前最后發送的2條數據 
    //replaySubject=ReplaySubject.createWithTime(1,TimeUnit.SECONDS,Schedulers.computation());  //replaySubject被訂閱前的前1秒內發送的數據才能被接收     
    replaySubject.onNext("replaySubject:pre1");
    replaySubject.onNext("replaySubject:pre2");
    replaySubject.onNext("replaySubject:pre3");
    replaySubject.subscribe(new Action1<String>() {
          @Override
          public void call(String s) {
                  LogUtil.log("replaySubject:" + s);
          }
      });
    replaySubject.onNext("replaySubject:after1");
    replaySubject.onNext("replaySubject:after2");
    

    以上代碼,由于情況比較多,注釋也已解釋的相當清楚,就不對輸出結果一一表述了,有疑問的自行copy代碼去測試一下。



Subject類型用作接受者(Observer)

至此,四種Subject類型已經介紹完畢,上文說過,Subject類型可以用作數據源(Observable),也可以用作接受源(Observer),或者兩者之間的橋梁。介紹四種Subject類型,就是當做數據源(Observable)來介紹的。這里不在舉例累贅。

但是需要注意,如果你把 Subject 當作一個 Observer(接受者)使用,不要從多個線程中調用它的onNext方法(包括其它的on系列方法),這可能導致同時(非順序)調用,這會違反Observable協議,給Subject的結果增加了不確定性。

  • 要避免此類問題,官方提出了“串行化”,你可以將 Subject 轉換為一個 SerializedSubject ,類似于這樣:(這個我沒試驗過,只是自己在資料中查到的方法)

    SerializedSubject<String, Integer> ser = new SerializedSubject(publishSubject);
    
  • 在實際開發中,用的多的是下邊這種橋梁轉發的方式。

    PublishSubject<String> publishSubject = PublishSubject.create();
    Observable.create(new Observable.OnSubscribe<String>() {
          @Override
          public void call(Subscriber<? super String> subscriber) {
    
              subscriber.onNext("as Observer"); 
              subscriber.onCompleted();
          }
    }).subscribe(publishSubject);
    

    有沒有發現問題?publishSubject沒有重寫onNext()方法啊,在哪接收的數據?這就是前面說的“橋梁”的問題了,盡管把Subject作為Observer傳入subscribe(),但接收數據還是要通過Observer來接收,借用Subject來連接Observable和Observer,整體代碼如下:

    PublishSubject<String> publishSubject = PublishSubject.create();
       Observable.create(new Observable.OnSubscribe<String>() {
              @Override
              public void call(Subscriber<? super String> subscriber) {
    
                  subscriber.onNext("as Bridge");
                  subscriber.onCompleted();
              }
          }).subscribe(publishSubject);
    
          publishSubject.subscribe(new Observer<String>() {
              @Override
              public void onCompleted() {
    
              }
    
              @Override
              public void onError(Throwable e) {
    
              }
    
              @Override
              public void onNext(String s) {
    
                  LogUtil.log("subject:"+s); //接收到 as Bridge
              }
          });
    

    沒錯,這很橋梁!


總結

關于Subject,到此就介紹完了。也許你會跟我一樣困惑,為什么又要多個Subject出來,除了有幾個特定功能之外,其他所有的一切,Observable和Observer也都有,而且寫法上也沒有原來的簡便。確實如此,對于幾個特定功能,我也還想不到有什么應用場景,至少我還沒發現有什么場景必須得用Subject來實現不可,那么問題又來了,我為什么要花這么大篇幅來介紹Subject,理由有三。其一,既然官方推出Subject,必有其道理,還沒遇到不代表以后不會遇到,更不能代表你不會遇到這樣的應用場景;其二,“一千個讀者有一千個哈姆雷特”,我所看到的并不是全部,也許你會發掘出更有意思的東西可不是?其三,我可不想當你看完我所有關于RxJava的文章,自信已上手RxJava,當有人跟你提起Subject的時候,你一臉茫然不知道Subject是什么東西,豈不哀哉?所以呢,介紹一下Subject還是很有意義的,最起碼學了比沒學好,“養兵千日用兵一時”,知識不嫌多,突然哪天就用上了呢。對于Subject的理解,有異議的歡迎底下評論,一起交流進步。下一篇文章,進入RxJava操作符的使用講解。

歡迎繼續收看:RxJava入門與提高-操作符篇(3)
作者:ZhangYushui
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容