本文介绍了RxJava处理业务异常的几种方式,分享给大家。具体如下:

关于异常

Java的异常可以分为两种:运行时异常和检查性异常。

运行时异常:

RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。

检查性异常:

除了RuntimeException及其子类以外,其他的Exception类及其子类都属于检查性异常。检查性异常必须被显式地捕获或者传递。当程序中可能出现检查性异常时,要么使用try-catch语句进行捕获,要么用throws子句抛出,否则编译无法通过。

处理业务异常

业务异常:

指的是正常的业务处理时,由于某些业务的特殊要求而导致处理不能继续所抛出的异常。在业务层或者业务的处理方法中抛出异常,在表现层中拦截异常,以友好的方式反馈给使用者,以便其可以依据提示信息正确的完成任务功能的处理。

1. 重试

不是所有的错误都需要立马反馈给用户,比如说在弱网络环境下调用某个接口出现了超时的现象,也许再请求一次接口就能获得数据。那么重试就相当于多给对方一次机会。

在这里,我们使用retryWhen操作符,它将错误传递给另一个被观察者来决定是否要重新给订阅这个被观察者。

听上去有点拗口,直接上代码吧。

/**

* 获取内容

* @param fragment

* @param param

* @param cacheKey

* @return

*/

public Maybe getContent(Fragment fragment, ContentParam param, String cacheKey) {

if (apiService == null) {

apiService = RetrofitManager.get().apiService();

}

return apiService.loadContent(param)

.retryWhen(new RetryWithDelay(3,1000))

.compose(RxLifecycle.bind(fragment).toLifecycleTransformer())

.compose(RxUtils.toCacheTransformer(cacheKey));

}

这个例子是一个网络请求,compose的内容可以忽略。如果网络请求失败的话,会调用retryWhen操作符。RetryWithDelay实现了Function接口,RetryWithDelay是一个重试的机制,包含了重试的次数和重试时间隔的时间。

import com.safframework.log.L;

import org.reactivestreams.Publisher;

import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;

import io.reactivex.annotations.NonNull;

import io.reactivex.functions.Function;

/**

* 重试机制

* Created by tony on 2017/11/6.

*/

public class RetryWithDelay implements Function, Publisher>> {

private final int maxRetries;

private final int retryDelayMillis;

private int retryCount;

public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {

this.maxRetries = maxRetries;

this.retryDelayMillis = retryDelayMillis;

this.retryCount = 0;

}

@Override

public Publisher> apply(@NonNull Flowable extends Throwable> attempts) throws Exception {

return attempts.flatMap(new Function>() {

@Override

public Publisher> apply(Throwable throwable) throws Exception {

if (++retryCount <= maxRetries) {

L.i("RetryWithDelay", "get error, it will try after " + retryDelayMillis

+ " millisecond, retry count " + retryCount);

// When this Observable calls onNext, the original

// Observable will be retried (i.e. re-subscribed).

return Flowable.timer(retryDelayMillis, TimeUnit.MILLISECONDS);

} else {

// Max retries hit. Just pass the error along.

return Flowable.error(throwable);

}

}

});

}

}

如果运气好重试成功了,那用户在无感知的情况下可以继续使用产品。如果多次重试都失败了,那么必须在onError时做一些异常的处理,提示用户可能是网络的原因了。

2. 返回一个默认值

有时出错只需返回一个默认值,有点类似Java 8 Optional的orElse()

RetrofitManager.get()

.adService()

.vmw(param)

.compose(RxLifecycle.bind(fragment).toLifecycleTransformer())

.subscribeOn(Schedulers.io())

.onErrorReturn(new Function() {

@Override

public VMWModel apply(Throwable throwable) throws Exception {

return new VMWModel();

}

});

上面的例子使用了onErrorReturn操作符,表示当发生错误的时候,发射一个默认值然后结束数据流。所以 Subscriber 看不到异常信息,看到的是正常的数据流结束状态。

跟它类似的还有onErrorResumeNext操作符,表示当错误发生的时候,使用另外一个数据流继续发射数据。在返回的被观察者中是看不到错误信息的。

使用了onErrorReturn之后,onError是不是就不做处理了?onErrorReturn的确是返回了一个默认值,如果onErrorReturn之后还有类似doOnNext的操作,并且doOnNext中出错的话,onError还是会起作用的。

曾经遇到过一个复杂的业务场景,需要多个网络请求合并结果。这时,我使用zip操作符,让请求并行处理,等所有的请求完了之后再进行合并操作。某些请求失败的话,我使用了重试机制,某些请求失败的话我给了默认值。

3. 使用onError处理异常

现在的Android开发中,网络框架是Retrofit的天下。在接口定义的返回类型中,我一般喜欢用Maybe、Completable来代替Observable。

我们知道RxJava在使用时,观察者会调用onNext、onError、onComplete方法,其中onError方法是事件在传递或者处理的过程中发生错误后会调用到。

下面的代码,分别封装两个基类的Observer,都重写了onError方法用于处理各种网络异常。这两个基类的Observer是在使用Retrofit时使用的。

封装一个BaseMaybeObserver

import android.accounts.NetworkErrorException

import android.content.Context

import com.safframework.log.L

import io.reactivex.observers.DisposableMaybeObserver

import java.net.ConnectException

import java.net.SocketTimeoutException

import java.net.UnknownHostException

/**

* Created by Tony Shen on 2017/8/8.

*/

abstract class BaseMaybeObserver : DisposableMaybeObserver() {

internal var mAppContext: Context

init {

mAppContext = AppUtils.getApplicationContext()

}

override fun onSuccess(data: T) {

onMaybeSuccess(data)

}

abstract fun onMaybeSuccess(data: T)

override fun onError(e: Throwable) {

var message = e.message

L.e(message)

when(e) {

is ConnectException -> message = mAppContext.getString(R.string.connect_exception_error)

is SocketTimeoutException -> message = mAppContext.getString(R.string.timeout_error)

is UnknownHostException -> message = mAppContext.getString(R.string.network_error)

is NetworkErrorException -> message = mAppContext.getString(R.string.network_error)

else -> message = mAppContext.getString(R.string.something_went_wrong)

}

RxBus.get().post(FailedEvent(message))

}

override fun onComplete() {}

}

封装一个BaseCompletableObserver

import android.accounts.NetworkErrorException

import android.content.Context

import com.safframework.log.L

import io.reactivex.observers.ResourceCompletableObserver

import java.net.ConnectException

import java.net.SocketTimeoutException

import java.net.UnknownHostException

/**

* Created by Tony Shen on 2017/8/8.

*/

abstract class BaseCompletableObserver : ResourceCompletableObserver() {

internal var mAppContext: Context

init {

mAppContext = AppUtils.getApplicationContext()

}

override fun onComplete() {

onSuccess()

}

abstract fun onSuccess()

override fun onError(e: Throwable) {

var message = e.message

L.e(message)

when(e) {

is ConnectException -> message = mAppContext.getString(R.string.connect_exception_error)

is SocketTimeoutException -> message = mAppContext.getString(R.string.timeout_error)

is UnknownHostException -> message = mAppContext.getString(R.string.network_error)

is NetworkErrorException -> message = mAppContext.getString(R.string.network_error)

else -> message = mAppContext.getString(R.string.something_went_wrong)

}

RxBus.get().post(FailedEvent(message))

}

}

在这里用到了Kotlin来写这两个基类,使用Kotlin的目的是因为代码更加简洁,避免使用switch或者各种if(XX instancof xxException)来判断异常类型,可以跟Java代码无缝结合。

下面的代码展示了如何使用BaseMaybeObserver,即使遇到异常BaseMaybeObserver的onError也会做相应地处理。如果有特殊的需求,也可以重写onError方法。

model.getContent(VideoFragment.this,param, cacheKey)

.compose(RxJavaUtils.maybeToMain())

.doFinally(new Action() {

@Override

public void run() throws Exception {

refreshlayout.finishRefresh();

}

})

.subscribe(new BaseMaybeObserver(){

@Override

public void onMaybeSuccess(ContentModel data) {

adpter.addDataToFront(data);

}

});

4. 内部异常使用责任链模式来分发

这是微信中一位网友提供的方法,他做了一个很有意思的用于异常分发的一个库,github地址:https://github.com/vihuela/Retrofitplus

内部异常使用责任链分发,分发逻辑为:

自定义异常->网络异常->服务器异常->内部程序异常->未知异常

除了以上自定义异常之外,此库包含其它异常分发,默认适应场景为:Rx+Json

自定义异常使用请调用,ExceptionParseMgr类的addCustomerParser方法添加业务异常

这个库对原先的代码无侵入性。此外,他还提供了另一种思路,结合compose来处理一些特定的业务异常。

总结

本文仅仅是总结了个人使用RxJava遇到业务异常的情况,并对此做了一些相应地处理,肯定是不能覆盖开发的方方面面,仅作为抛砖引玉,如果有更好、更优雅的处理方式,一定请告知。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

业务异常 java_浅谈RxJava处理业务异常的几种方式相关推荐

  1. .net mysql和php mysql数据库连接_浅谈PHP连接MySQL数据库的三种方式

    本篇文章给大家介绍一下PHP连接MySQL数据库的三种方式(mysql.mysqli.pdo),结合实例形式分析了PHP基于mysql.mysqli.pdo三种方式连接MySQL数据库的相关操作技巧与 ...

  2. aes key长度_原创 | 浅谈Shiro反序列化获取Key的几种方式

    点击"关注"了解更多信息 关于Apache Shiro反序列化 在shiro≤1.2.4版本,默认使⽤了CookieRememberMeManager,由于AES使用的key泄露, ...

  3. java 循环依赖_浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  4. 《浅谈F5健康检查常用的几种方式》—那些你应该知道的知识(二)

    负载均衡作为实现应用高可用和高可靠的一种方式,已成为目前数据中心内不可或缺的一个环节,并扮演着越来越重要的作用,而F5正是这一领域的佼佼者.要实现应用的高可用,如何探测负载均衡后端应用的可用性是其中非 ...

  5. python读取json数据格式问题_浅谈Python中的异常和JSON读写数据的实现

    异常可以防止出现一些不友好的信息返回给用户,有助于提升程序的可用性,在java中通过try ... catch ... finally来处理异常,在Python中通过try ... except .. ...

  6. python中怎么计数_浅谈python中统计计数的几种方法和Counter详解

    1) 使用字典dict() 循环遍历出一个可迭代对象中的元素,如果字典没有该元素,那么就让该元素作为字典的键,并将该键赋值为1,如果存在就将该元素对应的值加1. lists = ['a','a','b ...

  7. 单一修改高程值lisp_浅谈AutoCAD中修改高程的四种方法

    浅谈 AutoCAD 中修改高程的四种方法 摘 要: 在使用 AutoCAD 进行数字化成图工作中,经常遇到线划的标高不为零,及高程点的值与实地不符,需要对其进行修改等情况,结合实 际工作经验,简单介 ...

  8. android 系统升级 方法,安卓系统怎么升级 浅谈安卓系统更新升级的几种方法

    最近有网友问小编"安卓系统怎么升级?",针对该问题,笔者也在网上查找了下相关资料,不过并没有找到什么有价值的相关介绍,多数都是介绍如何自动升级.或者下载升级版包等等方法,对于一些常 ...

  9. 浅谈IM软件业务知识-实现富文本解析,如:解析字符串、网络链接等

    ----------------------------------------------------欢迎查看IM软件业务知识<专栏>-------------------------- ...

最新文章

  1. RT ROM boot简介
  2. Couchbase 集群小实践
  3. Innumerable Ancestors 尺取 dfs序 lca
  4. 【Python基础】Pandas笔记---深入Groupby,它的功能没有你想的这么简单
  5. go gin路由分组route group
  6. 如何在 SAP Spartacus 里添加自定义页面 - Custom Page
  7. python中的type函数-python的type函数
  8. Plugin 框架 开发实录
  9. QT4.7.3在dm6446平台上的移植[转]--make[1]: *** [assistant_cs.qm] Error 2
  10. iOS腾讯百度面试题
  11. 1941套站点模版,终生收藏,个个精品
  12. CAN通讯与RS485通讯区别
  13. linux中vi后如何退出命令,linux用VI编辑后保存退出命令是什么啊?
  14. matlab精简版如何使用,精简版快速入门Matlab.pdf
  15. WEB渗透测试之三大漏扫神器
  16. 程序员工资一般多少_一般程序员真实工资 程序员工资薪酬大起底
  17. (补)蒟蒻信安笔记1.5:(Nmap的使用部分)原来是这么个神奇的原因导致无法进行
  18. 英特尔推出全新RRP物联网平台 计划为零售技术投资一亿美元
  19. Qt编写自定义控件:带阴影、圆角、可拉伸的弹窗
  20. Word表格外的第一个空行如何删除

热门文章

  1. 如何输出字符串中的增补字符?-java
  2. 2020T电梯修理证考试及T电梯修理考试试题
  3. 多元线性回归的缺陷_回归分析|笔记整理(7)——多元线性回归(下),违背基本假设的情况...
  4. 小学三年计算机教案,小学信息技术教案三年级
  5. Linux CentOS 开启root用户远程登录
  6. innerHTML的属性
  7. UI一揽子计划 12 (模态ViewController、单例、通讯录实战、)
  8. 计算机控制面板没有笔和触摸,HP EliteBook 840 G1 笔记本电脑 - 虽然电脑不支持触控笔,但控制面板仍显示触控笔和触摸选项卡...
  9. async awit 异步调用的理解及应用
  10. jquery attr(“xxx“,“mmm“)修改标签属性的值