前言
歡迎繼續(xù)收看《RxJava入門與提高》砂吞,上周出了第一篇 RxJava入門與提高(1) 。
本文主要給大家補(bǔ)充一下上一篇遺留的Subject
知識喧半,沒看過上一篇的同學(xué)惧磺、忘了上一章寫什么的同學(xué)、還有其他同學(xué)赏寇,RxJava入門與提高(1)吉嫩。溫習(xí)一遍,俗話說嗅定,“書讀百遍自娩,奇異自見”,看多一遍是一遍渠退,多多益善嘛忙迁。溫習(xí)完的,請回來繼續(xù)聽講碎乃。
Subject
關(guān)于Subject姊扔,官方文檔的解釋是這樣的:Subject可以看成是一個(gè)橋梁或者代理,在某些ReactiveX實(shí)現(xiàn)中(如RxJava)梅誓,它同時(shí)充當(dāng)了Observer和Observable的角色旱眯。因?yàn)樗且粋€(gè)Observer,它可以訂閱一個(gè)或多個(gè)Observable;又因?yàn)樗且粋€(gè)Observable删豺,它可以轉(zhuǎn)發(fā)它收到(Observe)的數(shù)據(jù),也可以發(fā)射新的數(shù)據(jù)愧怜。從官方解釋中呀页,我提取出三個(gè)要點(diǎn):
它可以充當(dāng)Observable;
它可以充當(dāng)Observer拥坛;
它是Observable和Observer之間的橋梁蓬蝶;
接下來對這三個(gè)要點(diǎn)解釋一下,但在解釋之前猜惋,要先介紹一下Subject的種類丸氛, Subject是一個(gè)抽象類,不能通過new來實(shí)例化Subject著摔,所以Subject有四個(gè)實(shí)現(xiàn)類缓窜,分別為AsyncSubject、BehaviorSubject谍咆、PublishSubject和ReplaySubject禾锤,每個(gè)實(shí)現(xiàn)類都有特定的“技能”,下面結(jié)合代碼來介紹一下它們各自的“技能”摹察。注意恩掷,所有的實(shí)現(xiàn)類都由create()
方法實(shí)例化,無需new,所有的實(shí)現(xiàn)類調(diào)用onCompleted()
或onError()
,它的Observer將不再接收數(shù)據(jù)供嚎;
Subject的分類解析
-
AsyncSubject
Observer會接收AsyncSubject的``onComplete()`之前的最后一個(gè)數(shù)據(jù)黄娘,如果因異常而終止,AsyncSubject將不會釋放任何數(shù)據(jù)克滴,但是會向Observer傳遞一個(gè)異常通知逼争。示例代碼如下: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()被調(diào)用前的最后一個(gè)數(shù)據(jù)偿曙,即“asyncSubject3”氮凝,如果不調(diào)用onCompleted(),Subscriber將不接收任何數(shù)據(jù)望忆。
-
BehaviorSubject
Observer會接收到BehaviorSubject被訂閱之前的最后一個(gè)數(shù)據(jù)罩阵,再接收其他發(fā)射過來的數(shù)據(jù),如果BehaviorSubject被訂閱之前沒有發(fā)送任何數(shù)據(jù)启摄,則會發(fā)送一個(gè)默認(rèn)數(shù)據(jù)稿壁。(注意跟AsyncSubject的區(qū)別,AsyncSubject要手動(dòng)調(diào)用onCompleted()歉备,且它的Observer會接收到onCompleted()前發(fā)送的最后一個(gè)數(shù)據(jù)傅是,之后不會再接收數(shù)據(jù),而BehaviorSubject不需手動(dòng)調(diào)用onCompleted(),它的Observer接收的是BehaviorSubject被訂閱前發(fā)送的最后一個(gè)數(shù)據(jù)喧笔,兩個(gè)的分界點(diǎn)不一樣帽驯,且之后還會繼續(xù)接收數(shù)據(jù)。)示例代碼如下: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()
之前不發(fā)送behaviorSubject1嫌术、behaviorSubject2,則Observer會先接收到default,再接收behaviorSubject3牌借、behaviorSubject4度气。 -
PublishSubject
PublishSubject比較容易理解,相對比其他Subject常用膨报,它的Observer只會接收到PublishSubject被訂閱之后發(fā)送的數(shù)據(jù)磷籍。示例代碼如下: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會發(fā)射所有數(shù)據(jù)給觀察者,無論它們是何時(shí)訂閱的晒旅。也有其它版本的ReplaySubject栅盲,在重放緩存增長到一定大小的時(shí)候或過了一段時(shí)間后會丟棄舊的數(shù)據(jù)。示例代碼如下:ReplaySubject<String>replaySubject = ReplaySubject.create(); //創(chuàng)建默認(rèn)初始緩存容量大小為16的ReplaySubject废恋,當(dāng)數(shù)據(jù)條目超過16會重新分配內(nèi)存空間谈秫,使用這種方式,不論ReplaySubject何時(shí)被訂閱鱼鼓,Observer都能接收到數(shù)據(jù) //replaySubject = ReplaySubject.create(100);//創(chuàng)建指定初始緩存容量大小為100的ReplaySubject //replaySubject = ReplaySubject.createWithSize(2);//只緩存訂閱前最后發(fā)送的2條數(shù)據(jù) //replaySubject=ReplaySubject.createWithTime(1,TimeUnit.SECONDS,Schedulers.computation()); //replaySubject被訂閱前的前1秒內(nèi)發(fā)送的數(shù)據(jù)才能被接收 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");
以上代碼拟烫,由于情況比較多,注釋也已解釋的相當(dāng)清楚迄本,就不對輸出結(jié)果一一表述了硕淑,有疑問的自行copy代碼去測試一下。
Subject類型用作接受者(Observer)
至此嘉赎,四種Subject類型已經(jīng)介紹完畢置媳,上文說過,Subject類型可以用作數(shù)據(jù)源(Observable)公条,也可以用作接受源(Observer)拇囊,或者兩者之間的橋梁。介紹四種Subject類型靶橱,就是當(dāng)做數(shù)據(jù)源(Observable)來介紹的寥袭。這里不在舉例累贅路捧。
但是需要注意,如果你把 Subject 當(dāng)作一個(gè) Observer(接受者)使用传黄,不要從多個(gè)線程中調(diào)用它的onNext方法(包括其它的on系列方法)杰扫,這可能導(dǎo)致同時(shí)(非順序)調(diào)用,這會違反Observable協(xié)議尝江,給Subject的結(jié)果增加了不確定性涉波。
-
要避免此類問題,官方提出了“串行化”炭序,你可以將 Subject 轉(zhuǎn)換為一個(gè) SerializedSubject 鉴扫,類似于這樣:(這個(gè)我沒試驗(yàn)過空繁,只是自己在資料中查到的方法)
SerializedSubject<String, Integer> ser = new SerializedSubject(publishSubject);
-
在實(shí)際開發(fā)中,用的多的是下邊這種橋梁轉(zhuǎn)發(fā)的方式袱吆。
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);
有沒有發(fā)現(xiàn)問題相恃?publishSubject沒有重寫
onNext()
方法啊辜纲,在哪接收的數(shù)據(jù)?這就是前面說的“橋梁”的問題了拦耐,盡管把Subject作為Observer傳入subscribe()
耕腾,但接收數(shù)據(jù)還是要通過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 } });
沒錯(cuò)扫俺,這很橋梁!
總結(jié)
關(guān)于Subject固翰,到此就介紹完了狼纬。也許你會跟我一樣困惑,為什么又要多個(gè)Subject出來骂际,除了有幾個(gè)特定功能之外疗琉,其他所有的一切,Observable和Observer也都有歉铝,而且寫法上也沒有原來的簡便盈简。確實(shí)如此,對于幾個(gè)特定功能太示,我也還想不到有什么應(yīng)用場景柠贤,至少我還沒發(fā)現(xiàn)有什么場景必須得用Subject來實(shí)現(xiàn)不可,那么問題又來了先匪,我為什么要花這么大篇幅來介紹Subject种吸,理由有三。其一呀非,既然官方推出Subject坚俗,必有其道理镜盯,還沒遇到不代表以后不會遇到,更不能代表你不會遇到這樣的應(yīng)用場景猖败;其二速缆,“一千個(gè)讀者有一千個(gè)哈姆雷特”,我所看到的并不是全部恩闻,也許你會發(fā)掘出更有意思的東西可不是艺糜?其三,我可不想當(dāng)你看完我所有關(guān)于RxJava的文章幢尚,自信已上手RxJava破停,當(dāng)有人跟你提起Subject的時(shí)候,你一臉茫然不知道Subject是什么東西尉剩,豈不哀哉真慢?所以呢,介紹一下Subject還是很有意義的理茎,最起碼學(xué)了比沒學(xué)好黑界,“養(yǎng)兵千日用兵一時(shí)”,知識不嫌多皂林,突然哪天就用上了呢朗鸠。對于Subject的理解,有異議的歡迎底下評論础倍,一起交流進(jìn)步烛占。下一篇文章,進(jìn)入RxJava操作符的使用講解著隆。
歡迎繼續(xù)收看:RxJava入門與提高-操作符篇(3)
作者:ZhangYushui
來源:簡書
著作權(quán)歸作者所有扰楼。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處美浦。