博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RxJava2的错误处理方案
阅读量:6648 次
发布时间:2019-06-25

本文共 4935 字,大约阅读时间需要 16 分钟。

hot3.png

最近使用retrofit2 + rxKotlin2写接口访问,想尽量平铺代码,于是就想到当借口返回的状态码为「不成功」时(比如:code != 200),就连同网络错误一起,统一在onError方法中处理。想法总是好的,但是实际中却遇到onError无法捕获异常,造成应用崩溃的问题,终于在这个周末,我梳理清楚了RxJava的错误处理机制。

每一个后端接口基本都会有一个自定义的返回码,我们常常依据返回码来判断接口是否访问成功,是否成功返回我们想要的数据,当初作为一只菜鸟,我是这样来写的:

fun login(account: String, pwd: String) {        showProgress("登陆中")//展示菊花        service.login(account,pwd)                .subscribeOn(Schedulers.io())				.observeOn(AndroidSchedulers.mainThread())                .subscribe({                    if (it.code == 20000 && it.data != null) {                        //登陆成功!						//跳转到主页                    } else {						//登陆失败!						//弹出提示						//dismissProgress()关闭菊花                    }                }, {                    dismissProgress()                    //登陆失败!					//弹出提示						//dismissProgress()关闭菊花                })    }

这样写并没有什么问题,但是让大佬看到一定会笑,因为在onSuccess或onNext方法中,既有成功的逻辑,又有失败的逻辑,存在耦合性,并且处理接口访问失败的逻辑被分割成了两部分,那就等于失败的逻辑有一部分被混进了成功的逻辑,那么有没有什么办法能统一处理访问失败的逻辑呢?

巧用map操作符解偶

fun login(account: String, pwd: String) {        showProgress("登陆中")//展示菊花        service.login(account,pwd)                .subscribeOn(Schedulers.io())				.map{ //注意这里的map					if (it.code != 20000 || it.data == null) {						throw Exception("登陆失败!")					}					it				}				.observeOn(AndroidSchedulers.mainThread())                .subscribe({                        //登陆成功!						//跳转到主页                }, {                    dismissProgress()                    //登陆失败!					//弹出提示						//dismissProgress()关闭菊花                })    }

我们看到,当在线程切换前,我们可以在map操作符逻辑中来判断返回码,如果返回码表示成功,那么就直接返回原数据,并且传递到onSuccess/onNext方法;如果返回码为失败的话,就手动抛出一个异常,然后在订阅中,onError方法能够捕获到这个异常,这样我们手动抛出的异常就可以与请求超时、404这种网络及的错误一起来处理,而onSuccess或者onNext方法中只需要处理访问成功的逻辑,成功完成了解偶,看起来是不是很清晰!

这里有一点要注意,在map逻辑必须要throw出exception,而不能直接返回exception,否则rxjava会认为你想把原数据转换成Exception类型的数据,并传递到onSuccess/onNext方法中

直调onError

有些时候业务逻辑可能会非常简单,接口也只包含了返回码或其他代表请求结果的信息,而没有给出具体的实际数据,而这时候聪明的你又想偷懒,那我们可以这样来写:

fun login(account: String, pwd: String) {        showProgress("登陆中")//展示菊花        service.login(account,pwd)                .subscribeOn(Schedulers.io())				.observeOn(AndroidSchedulers.mainThread())                .subscribe({					if (it.code != 20000 || it.data == null) {						onError(Exception("登陆失败!"))//注意这里					}                    //登陆成功!					//跳转到主页                }, {                    dismissProgress()                    //登陆失败!					//弹出提示						//dismissProgress()关闭菊花                })    }

这里在返回码为失败的时候,直接调用了onError并且传递了一个exception,也是可以的。但是如果像map操作符中那样直接throw是不行。因为onError与onSuccess/onNoext平级,在onSuccess/onNext中直接抛出异常并没有其他方法来捕获,这样就很容易导致应用崩溃。

map配合onExceptionResumeNext

fun login(account: String, pwd: String) {        showProgress("登陆中")//展示菊花        service.login(account,pwd)                .subscribeOn(Schedulers.io())				.map{ //注意这里的map					if (it.code != 20000 || it.data == null) {						throw Exception("登陆失败!")					}					it				}				.onExceptionResumeNext {                    service.register(account,pwd,it)//把登陆操作转换成注册操作                }				.observeOn(AndroidSchedulers.mainThread())                .subscribe({					if (it.code != 20000 || it.data == null) {						onError(Exception("失败!"))//注意这里					}                    //成功!					//跳转到主页                }, {                    dismissProgress()                    //失败!					//弹出提示						//dismissProgress()关闭菊花                })    }

onExceptionResumeNext操作符其实与flatmapflatmap操作很像,都是用来转换目标的,不同的是,onErrorResumeNext具有捕获异常的能力,而且仅当捕获到异常Exception后才会被调用。上例中我们就利用onErrorResumeNext达到了「登陆失败后自动注册」效果。

onErrorResumeNext

onErrorResumeNext操作符的用法与onExceptionResumeNext基本一样,但不同的是,后者仅捕获Exception,而前者只要是Throwable就捕获。,所以还是更推荐优先使用onExceptionResumeNext

onErrorReturn

private fun test5() {        Single.just(0)                .map {                    if (it == 0) {                        throw Exception("我擦")                    }                    1                }                .onErrorReturn {                    2                }                .subscribe({                    Log.d("测试", it.toString())                }, {                    Log.e("测试", it.message)                })    }

onErrorReturn本身与map很像,都是用来转换原数据的,不同的是,onErrorReturn也具有捕获异常的能力,而且仅当捕获到异常后才会被调用。onErrorReturn可以理解为:遭遇异常后返回一个默认值传递到onNext方法,然后调用onComplete方法终止发射。(这个操作符太好玩了)

retry

private fun test12() {        Observable.range(0, 100)                .map {                    if (it == 0) {                        throw Exception("我擦")                    }                    Log.d("测试","仍在发射${it}")                    it                }                .retry { t1, t2 ->                    t1 != 50                }                .subscribe({                    Log.d("测试", it.toString())                }, {                    Log.e("测试", it.message)                })    }

retry操作符顾名思义就是用来重试,但是重试的条件是我们来定的,所以思维联想一下,就知道用这个操作符做轮询是很方便的!除了retry之外,rxjava还提供了retryWhenretryUntil等类似的操作符,与retry意思差不多,就不做解释了,感兴趣的同学可以自己去看。

Demo

转载于:https://my.oschina.net/JiangTun/blog/1860963

你可能感兴趣的文章
策略模式-鸭子怎么飞-实例
查看>>
01.Apache FtpServer配置
查看>>
Common Lisp学习笔记(七)
查看>>
syscomments 原始的 SQL 定义语句
查看>>
mvn 手动打包并放置到本地仓库下
查看>>
js计算24点
查看>>
软件开发基础常识
查看>>
安装Ubuntu时出现Intel VT-X没有开启
查看>>
XML中的url链接写法
查看>>
洛谷P1119 灾后重建
查看>>
ArcSDE:"Bad Login User" 错误解决方法
查看>>
android
查看>>
jasypt-spring-boot
查看>>
(诊断)为GitHub添加SSH key时出现“Could not open a connection to your authentication agent”错误的应对方案(转)...
查看>>
彼得原理
查看>>
30分钟让你了解MongoDB基本操作(转)
查看>>
用户交互程序
查看>>
Python学习【第17篇】:网络编程之粘包
查看>>
容器内部安装scp,拷贝到外部物理机
查看>>
微信js分享朋友圈(一)
查看>>