RxJava 謹慎串聯(lián)Observable

問題

RxJava提供了flatMap和switchMap兩個操作符用于讓我們進行Observable的串聯(lián)脐雪,比如我們可以使用RxView.clicks()創(chuàng)建一個會發(fā)送點擊事件的Observable,同時我們還有一個用于請求網(wǎng)絡數(shù)據(jù)的Observable:

Observable<Void> loginPress(){
  return RxView.clicks(findViewById(R.id.login));
}

Observable<LoginInfo> login() {
  return httpApi.login();
}

需求希望在login按鈕點下之后饮潦,調(diào)用login()方法進行登陸。此時有兩種寫法:

  1. 直接串聯(lián):
loginPress().flatMap(aVoid -> login()).subscribe(loginInfo -> {
  // 處理登錄邏輯
}, throwable -> {
  // 處理錯誤情況
});
  1. 分別調(diào)用:
loginPress().subscribe(aVoid -> {
  login().subscribe(loginInfo -> {
    // 處理登錄邏輯
  }, throwable -> {
    // 處理錯誤情況
  });
});

從代碼上看吕嘀,第一種方式顯然是Rx更為推薦的——不打破鏈式調(diào)用 的方式陡舅。但在有些時候酌住,這種方法會出現(xiàn)比較嚴重的問題:原因是,subscriber在接受到錯誤以后刃麸,就無法接受到之后的事件了醒叁。

舉上面的第一個使用例子來說有兩個問題:

  1. 如果處理登錄邏輯里發(fā)生了一些意料不到的錯誤(比如服務器有時候成功返回了數(shù)據(jù),但有些數(shù)據(jù)為空導致了處理邏輯出現(xiàn)空指針)泊业,發(fā)生錯誤時把沼,錯誤會回調(diào)到throwable->{}中。之后再進行按鈕點擊脱吱,數(shù)據(jù)返回subscriber都接收不到了智政。

  2. 如果login()方法里有錯誤,比如網(wǎng)絡訪問異常箱蝠。那么當?shù)谝淮吸c擊按鈕時续捂,subscriber會收到網(wǎng)絡異常的錯誤。但如果用戶再點擊登錄按鈕宦搬,無論是否成功牙瓢,我們都沒有辦法再次接受到登錄信息,頁面也無法發(fā)生跳轉间校。

前者在使用flatMap或者switchMap會發(fā)生矾克,而后者在任何情況下都有可能出現(xiàn)。

解決方案

使用方案2憔足,分別調(diào)用不會產(chǎn)生相應的問題胁附。但打破了RxJava的鏈式調(diào)用。

對于使用方案1滓彰,最簡單的解決方案是:在遇到錯誤重新綁定控妻。但這種方式的成本比較高。每個處理訂閱的地方都需要進行特殊處理揭绑。

首先是第一個問題:

  1. 如果處理登錄邏輯里發(fā)生了一些意料不到的錯誤(比如服務器有時候成功返回了數(shù)據(jù)弓候,但有些數(shù)據(jù)為空導致了處理邏輯出現(xiàn)空指針)郎哭,發(fā)生錯誤時,錯誤會回調(diào)到throwable->{}中菇存,但之后的任何數(shù)據(jù)返回subscriber都接不到了夸研。

這種情況出現(xiàn)的其實比較少。對于這種不可意料的錯誤依鸥,我們可以使用一個大大的try-catch把subscriber包起來亥至,比如實現(xiàn)一個類似這樣的類:

public class ErrorHandlerSubscriber<T> extends Subscriber<T> {
  private Action1<T> onNext;
  private Action1<Throwable> onError;

  public ErrorHandlerSubscriber(Action1<T> onNext, Action1<Throwable> onError) {
    this.onNext = onNext;
    this.onError = onError;
  }

  @Override
  public void onCompleted() {}

  @Override
  public void onError(Throwable e) {
    if (onError != null) {
        onError.call(e);    
    }
  }

  @Override
  public void onNext(T t) {
    try {
        if (onNext != null) {
            onNext.call(t);
        }
    } catch (Exception e) {
        if (onError != null) {
            onError.call(e);
        } else {
          // log it
        }
    }}
}

在使用時:

login().subscribe(new ErrorHandlerSubscriber(loginInfo -> {
    // 處理登錄邏輯
  } , throwable -> {
    // 處理失敗
  }));

這樣一來,錯誤實際上不會被轉發(fā)到Subscriber內(nèi)毕籽,而只是會傳到我們自定義的throwable -> {}里抬闯。也就不會影響實際Subscriber后續(xù)事件的接受井辆。

同時关筒,建議在// log it的地方將錯誤日志打出來,方便調(diào)試杯缺。


對于第二個問題:

2.如果login()方法里有錯誤蒸播,比如網(wǎng)絡訪問異常祠够。那么當?shù)谝淮吸c擊按鈕時圈浇,subscriber會收到網(wǎng)絡異常的錯誤。但如果用戶再點擊登錄按鈕溉痢,無論是否成功塘揣,我們都沒有辦法再次接受到登錄信息包雀,頁面也無法發(fā)生跳轉。

這種情況出現(xiàn)出現(xiàn)會十分頻繁亲铡,尤其在進行網(wǎng)絡請求時才写。解決方案有N種

1.如果你不關心錯誤,可以使用switchMapDelayError

這個關鍵字可以起到忽略錯誤的作用奖蔓,但大部分情況下赞草,我們希望在遇到錯誤對用戶進行提示。所以如果你不關心錯誤是否發(fā)生的情況下吆鹤,使用這個關鍵字進行串聯(lián)是最簡單的厨疙。

2.使用materialize()將next和error都包裝到notification中:

http://stackoverflow.com/questions/32084824/rxjava-rxbinding-how-to-handle-errors-on-rxview

loginPress().flatMap(aVoid -> login().materialize()).subscrber(notification -> {
  if(notification.hasValue()){
    // 處理登錄邏輯
  } else if(notification.isOnError()) {
    // 處理失敗邏輯 
  }
});

3.使用doOnError處理錯誤,同時使用onErrorResumeNext忽略錯誤:

loginPress().flatMap(aVoid -> login().doOnError(throwable -> {
    // 處理失敗邏輯 
  }).onErrorResumeNext(throwable -> Observable.empty())
  ).subscriber(loginInfo -> {
    // 處理登錄邏輯
  } , throwable -> {
    // 處理失敗
  });

這樣一來疑务,flatMap里的Observable實際上就不會發(fā)生錯誤沾凄,也就不會造成相應的問題了。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末知允,一起剝皮案震驚了整個濱河市撒蟀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廊镜,老刑警劉巖牙肝,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡配椭,警方通過查閱死者的電腦和手機虫溜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來股缸,“玉大人衡楞,你說我怎么就攤上這事《匾觯” “怎么了瘾境?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镰惦。 經(jīng)常有香客問我迷守,道長,這世上最難降的妖魔是什么旺入? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任兑凿,我火速辦了婚禮,結果婚禮上茵瘾,老公的妹妹穿的比我還像新娘礼华。我一直安慰自己,他們只是感情好拗秘,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布圣絮。 她就那樣靜靜地躺著,像睡著了一般雕旨。 火紅的嫁衣襯著肌膚如雪扮匠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天奸腺,我揣著相機與錄音餐禁,去河邊找鬼。 笑死突照,一個胖子當著我的面吹牛帮非,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讹蘑,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼末盔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了座慰?” 一聲冷哼從身側響起陨舱,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎版仔,沒想到半個月后游盲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體误墓,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年益缎,在試婚紗的時候發(fā)現(xiàn)自己被綠了谜慌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡莺奔,死狀恐怖欣范,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情令哟,我是刑警寧澤恼琼,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站屏富,受9級特大地震影響晴竞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜役听,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一颓鲜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧典予,春花似錦、人聲如沸乐严。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昂验。三九已至捂敌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間既琴,已是汗流浹背占婉。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甫恩,地道東北人逆济。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像磺箕,于是被迫代替她去往敵國和親奖慌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 在正文開始之前的最后松靡,放上GitHub鏈接和引入依賴的gradle代碼: Github: https://gith...
    蘇蘇說zz閱讀 678評論 0 2
  • 文章轉自:http://gank.io/post/560e15be2dca930e00da1083作者:扔物線在正...
    xpengb閱讀 7,032評論 9 73
  • 在正文開始之前的最后简僧,放上 GitHub 鏈接和引入依賴的 gradle 代碼: Github: https://...
    松江野人閱讀 5,893評論 0 1
  • 使用體驗 描述 舉個例子:用戶開啟程序,不用等待就能看到課程信息雕欺。向下滑動岛马,后臺自動獲取更多棉姐,不用等待加載過程…這...
    liangtong閱讀 304評論 0 0
  • 改變的第一步,增加自己渴望的第一步就是學會對別人發(fā)出哇啦逆,如果你每次看到別人好谅海,你都發(fā)出呸,每次看到別人有貴人的幫忙...
    77天賦臻月閱讀 297評論 0 0