RxJava | 错误处理 Operators

RxJava.png

0. 概述

感谢:对RxJava中.repeatWhen()和.retryWhen()操作符的思考

本文章讲解的是RxJava1.0版本中的错误处理的操作符,
其操作符主要有两个:
Catch:从onError通知中恢复发射数据
Retry:原始Observable遇到错误,重新订阅它期望它能正常终止


1. Retry

Retry,顾名思义,是重试。当原始调用链抛出了onError事件,则,就会
触发Retry。
在RxJava中,关于Retry,有几个版本:

  1. retry() 没有传入参数,若有onError事件,则一直重试。
  2. retry(long) 传入重试的次数。
  3. retry(Func) 这个函数的两个参数是:重试次数和导
    致发射 onError 通知的 Throwable 。这个函数返回一个布尔值,如果返回 true , retry 应该
    再次订阅和镜像原始的Observable,如果返回 false , retry 会将最新的一个 onError 通知
    传递给它的观察者。

我们来看具体的例子:

  • retry():没有传入参数,若有onError事件,则一直重试。
Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"getInt:"+integer);
            int a = 0;
            int b = 1;
            int c = b/a;
            return integer + 10;
        })
        .retry()
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"error:"+(throwable.getMessage()!=null?throwable.getMessage():"null"));
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onComplete");
                });

这里,我们发生了一个整数1,在Map里面故意制造了一个onError事件,
如果,没有onError,retry()不会起效果,把接收到的数据继续往下传。
在这里,出现了onError,因为是retry(),所以会不断地重试。

  • retry(long):传入重试的次数。
Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"getInt:"+integer);
            int a = 0;
            int b = 1;
            int c = b/a;
            return integer + 10;
        })
        .retry(3)
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"error:"+(throwable.getMessage()!=null?throwable.getMessage():"null"));
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onComplete");
                });

这里的输出是:

12-18 14:38:05.434 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:38:05.437 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:38:05.437 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:38:05.438 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:38:05.438 11348-11348/testmodules.chestnut E/MainActivity: error:divide by zero

可以看到,重试了3次。

  • retry(Func): 这个函数的两个参数是:integer, throwable,当前重试的次数和错误体。
    返回值:true:进入重试,false:结束重试,并把最新一次的throwable传递。
Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"getInt:"+integer);
            int a = 0;
            int b = 1;
            int c = b/a;
            return integer + 10;
        })
        .retry((integer, throwable) -> {
            LogUtils.e(OpenLog,TAG,"retry:"+integer);
            if (integer>=3)
                return false;
            else
                return true;
        })
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"error:"+(throwable.getMessage()!=null?throwable.getMessage():"null"));
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onComplete");
                });

在retry里面,我们自定义了一个规则,当重试的次数达到3次,我们就返回false,也就是停止重试了。
输出:

12-18 14:40:20.950 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:40:20.951 11348-11348/testmodules.chestnut E/MainActivity: retry:1
12-18 14:40:20.951 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:40:20.952 11348-11348/testmodules.chestnut E/MainActivity: retry:2
12-18 14:40:20.952 11348-11348/testmodules.chestnut E/MainActivity: getInt:1
12-18 14:40:20.952 11348-11348/testmodules.chestnut E/MainActivity: retry:3
12-18 14:40:20.953 11348-11348/testmodules.chestnut E/MainActivity: error:divide by zero
  • retryWhen(Func1)
    **retryWhen(Func1<? super Observable<? extends java.lang.Throwable>,? extends Observable<?>> notificationHandler)**
    我们先明确:

1 .Func1像个工厂类,用来实现你自己的重试逻辑。
2 .输入的是一个Observable<Throwable>。
3 .输出的是一个Observable<?>。

如果发送的是onCompleted或者onError事件,将不会触发重订阅。相对的,如果它发送onNext事件,则触发重订阅(不管onNext实际上是什么事件)。
其实这个跟retry差不多,只是每次把throwable用Obserable包裹了起来,当收到onError事件的时候,你可以直接在里面使用链式去判断,是否重试。

Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"getInt:"+integer);
            int a = 1/0;
            return integer + 10;
        })
        .retryWhen(observable -> {
            return Observable.error(new Throwable("终止重试条件"));   //终止重试条件:发射onComplete或者error
        })
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"error:"+(throwable.getMessage()!=null?throwable.getMessage():"null"));
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onComplete");
                });

以上,在遇到onError后,直接是把Throwable再度抛出结束。

Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"getInt:"+integer);
            int a = 1/0;
            return integer + 10;
        })
        .retryWhen(new Func1<Observable<? extends Throwable>, Observable<String>>() {
            @Override
            public Observable<String> call(Observable<? extends Throwable> observable) {
                return observable.map(o -> {
                    LogUtils.e(OpenLog,TAG,"retryWhen:"+(o.getMessage()!=null?o.getMessage():"null"));
                    return "retryWhen,error";
                });
            }
        })
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"error:"+(throwable.getMessage()!=null?throwable.getMessage():"null"));
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onComplete");
                });

上面这个,直接对错误链,用map操作符输出了一下,然后重试,其效果跟无参数的retry()差不多。

  • retryWhen(Func1,Scheduler)
    这个版本,可以制定其运行的调度器。默认的是:trampoline

2. Catch

catch和程序中的catch是一样的,都是捕抓异常。
在链式的传递中,当其发生错误时onError的时候,我们希望不终止链式的传递,而是希望根据
异常去恢复链式。在Java中,Catch被实现成:onErrorReturn.

  • onErrorReturn
    **Observable<T> onErrorReturn(Func1<? super Throwable, ? extends T> resumeFunction)**
  • 由方法的定义可以看到,其传入异常体:throwable,然后返回一个异常前的对象体:? extends T
  • onErrorReturn 方法返回一个镜像原有Observable行为的新Observable,后者会忽略前者
    的 onError 调用,不会将错误传递给观察者,作为替代,它会发发射一个特殊的项并调用观
    察者的 onCompleted 方法。

例子:

Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"map:"+integer);
            int a = 1;
            int b = 0;
            int c = a/b;
            return integer;
        })
        .onErrorReturn(new Func1<Throwable, Integer>() {
            @Override
            public Integer call(Throwable throwable) {
                LogUtils.e(OpenLog,TAG,"onErrorReturn:"+throwable.getMessage());
                return 12321;
            }
        })
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"onError:"+throwable.getMessage());
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onCompleted:");
                });

Log输出如下:

12-19 16:47:10.686 8522-8522/testmodules.chestnut E/MainActivity: map:1
12-19 16:47:10.690 8522-8522/testmodules.chestnut E/MainActivity: onErrorReturn:divide by zero
12-19 16:47:10.691 8522-8522/testmodules.chestnut E/MainActivity: onNext:12321
12-19 16:47:10.691 8522-8522/testmodules.chestnut E/MainActivity: onCompleted:
  • onErrorResumeNext
    和onErrorReturn一样,只不过,它返回的是一个新的Observable。
Observable.just(1)
        .map(integer -> {
            LogUtils.e(OpenLog,TAG,"map:"+integer);
            int a = 1;
            int b = 0;
            int c = a/b;
            return integer;
        })
        .onErrorResumeNext(new Func1<Throwable, Observable<? extends Integer>>() {
            @Override
            public Observable<? extends Integer> call(Throwable throwable) {
                return Observable.just(123);
            }
        })
        .subscribe(
                integer -> {
                    LogUtils.e(OpenLog,TAG,"onNext:"+integer);
                },
                throwable -> {
                    LogUtils.e(OpenLog,TAG,"onError:"+throwable.getMessage());
                },
                () -> {
                    LogUtils.e(OpenLog,TAG,"onCompleted:");
                });
  • onExceptionResumeNext
    和 onErrorResumeNext 类似, onExceptionResumeNext 方法返回一个镜像原有Observable行为
    的新Observable,也使用一个备用的Observable,不同的是,如果 onError 收到
    的 Throwable 不是一个 Exception ,它会将错误传递给观察者的 onError 方法,不会使用备用
    的Observable。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 怎么如此平静, 感觉像是走错了片场.为什么呢, 因为上下游工作在同一个线程呀骚年们! 这个时候上游每次调用emit...
    Young1657阅读 1,440评论 2 1
  • 作者: maplejaw本篇只解析标准包中的操作符。对于扩展包,由于使用率较低,如有需求,请读者自行查阅文档。 创...
    maplejaw_阅读 45,594评论 8 93
  • 注:只包含标准包中的操作符,用于个人学习及备忘参考博客:http://blog.csdn.net/maplejaw...
    小白要超神阅读 900评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • observable 作为RxJava总要组成部分,observable 相当于观察者模式中的被观察者。下面详细介...
    Scott_he阅读 18,188评论 1 2