业务异常 java_谈谈RxJava处理业务异常的几种方式
此文介绍了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遇到业务异常的情况,并对此做了一些相应地处理,肯定是不能覆盖开发的方方面面,仅作为抛砖引玉,如果有更好、更优雅的处理方式,一定请告知。
上面即是这篇文章的内容,希望对各位的学习有所启发,也希望大家多多支持学猫在线(shtml.net)。
本文来源:http://www.jianshu.com/p/423cc558556b
业务异常 java_谈谈RxJava处理业务异常的几种方式相关推荐
- storyboard搭建项目_简单谈谈ios程序界面实现的三种方式(代码创建,xib和storyboard)...
一丶前言 实现ios界面总的来说,有三种方式,传统的是纯代码创建与xib创建,近年来,苹果官网一直推荐用storyboard管理项目界面,最新的xcode 创建的project也是默认为storybo ...
- 业务异常 java_浅谈RxJava处理业务异常的几种方式
本文介绍了RxJava处理业务异常的几种方式,分享给大家.具体如下: 关于异常 Java的异常可以分为两种:运行时异常和检查性异常. 运行时异常: RuntimeException类及其子类都被称为运 ...
- android如何获取网络的状态码,Android RxJava+Retrofit网络异常、状态码统一处理
Android RxJava+Retrofit 网络异常捕获.状态码统一处理 前言 近来使用RxJava+Retrofit进行开发,在项目中遇到这样一个需求,联网请求获得数据异常时,需要将对应的Mes ...
- 谈谈 To B业务的机会
谈谈To B业务的难点 这篇文章写的时候觉得能有2万阅读就不错了,你看,没有追热点,没有炫标题,真没想到,到现在接近18万的阅读,是我公众号历史访问量最高的一篇. 意外之余也在反思,为什么这么多人关心 ...
- 农分期 java_农分期现行业务大揭密,8项业务为农户提供全方向服务
原标题:农分期现行业务大揭密,8项业务为农户提供全方向服务 "农分期,怎么办?"兄弟,农分期业务太多了,你问我怎么办,我该怎么回答你呢? 所以今天我得帮大家把农分期能替你解决的问题 ...
- 业务层 java_表现层(jsp)、持久层(类似dao)、业务层(逻辑层、service层)、模型(javabean)、控制层(action)...
为了实现web层(struts)和持久层(Hibernate)之间的松散耦合,我们采用业务代表(Business Delegate)和DAO(Data Access Object)两种模式.DAO模式 ...
- JAVA中的异常的触发_java中的异常
在日常的程序开发中难免会出现遗漏并且就算代码没有问题可是由于程序运行环境的内存不够了,磁盘满了,网络连接问题等这些非正常的情况在java中都称之为异常.在java中对异常的处理有统一的异常处理机制,今 ...
- struts启动过滤器异常_面试必备:网关异常了怎么办?如何做全局异常处理?
欢迎关注头条号:老顾聊技术 精品原创技术分享,知识的组装工 目录 前言 为什么要自定义异常处理 如何自定义异常处理 测试 总结 前言 我们平时在用SpringMVC的时候,只要是经过Dispatche ...
- java 必须try catch的异常_【java基础之异常】死了都要try,不淋漓尽致地catch我不痛快!...
@ 1.异常 1.1 异常概念 异常 :简单说就是不正常运行,最终导致JVM的非正常停止. 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象.Java处 ...
最新文章
- 资源 | 阿里发布免费深度学习课程:感知机梳理(附链接)
- Android 切换系统语言源码分析
- [BZOJ 1124][POI 2008] 枪战 Maf
- PAT1045 快速排序 (25 分)【4/6通过】
- 入门深度学习,其实并不难!
- QString 字符编码
- mysql的limit_MYSQL中LIMIT用法
- 【nodejs原理源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick
- linux java jdk配置_Linux环境下安装JDK并配置环境变量
- 【拾贝】hive unoin all map数爆增
- 聊天室显示在线人数和已上线人数
- Python 数据结构与算法 —— 链表
- Docker安装nginx以及负载均衡
- 永磁同步电机的原理介绍
- J-Flash下载STM32用J-link的设置方法
- ISCC2017 Misc write up附件题目文件
- 2020年3月全国程序员工资统计,平均工资13820元
- JS 监听 storage
- 前端,值得收藏的那些网站
- java.lang.reflect.InvocationTargetException异常处理方法
热门文章
- Perl,Python,Ruby,Javascript 四种脚本语言比较
- 简明Python3教程 16.标准库
- mysql-安装报错计算机中丢失MSVCR100.dll文件丢失
- 【数据结构与算法】字符串匹配 BM算法
- 请对比html与css的异同,css3与css2的区别是什么?
- datetime mysql 当天_MySQL 获得当前日期时间(以及时间的转换)
- 《C语言深度解剖》中的.c/.h 程序模板及函数注释风格
- android电视打印信息解析,关于液晶电视打印信息
- php实战搭建博客,yii2项目实战-博客管理平台的搭建
- LSGO软件技术团队2015~2016学年第十二周(1116~1122)总结