业务异常 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第二节:重写(new)、覆写(overwrite)、和重载(overload)

一. 重写 1. 关键字&#xff1a;new 2. 含义&#xff1a;子类继承父类中的普通方法&#xff0c;如果在子类中重写了一个和父类中完全相同的方法&#xff0c;子类中会报警告(问是否显式的隐藏父类的中的方法)&#xff0c;如果在子类中的方法前加上new关键字&#xff0c;则警告…

java 分页查询_JavaWeb之分页查询

时间&#xff1a;2016-12-11 01:411、分页的优点&#xff1a;只查询一页&#xff0c;不需要查询所有数据&#xff0c;能够提高效率。2、分页数据页面的数据都是由Servlet传递的* 当前页&#xff1a;pageCode> 如果页面没有向Servlet传递页码&#xff0c;那么Servlet默认…

第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

一. 各类数据结构比较及其线程安全问题 1. Array(数组)&#xff1a; 分配在连续内存中,不能随意扩展&#xff0c;数组中数值类型必须是一致的。数组的声明有两种形式&#xff1a;直接定义长度&#xff0c;然后赋值&#xff1b;直接赋值。 缺点&#xff1a;插入数据慢。 优点&a…

java万法_Java I/O库的设计分析

Java采用了流的机制来实现输入&#xff0f;输出。所谓流&#xff0c;就是数据的有序排列。而流可以是从某个源(称为流源或Source of Stream)出来&#xff0c;到某个目的地(称为流汇或Sink of Stream)去的。由流的方向&#xff0c;可以分成输入流和输出流。一个程序从输入流读取…

第四节:IO、序列化和反序列化、加密解密技术

一. IO读写   这里主要包括文件的读、写、移动、复制、删除、文件夹的创建、文件夹的删除等常规操作。 注意&#xff1a;这里需要特别注意&#xff0c;对于普通的控制台程序和Web程序&#xff0c;将"相对路径"转换成"绝对路径"的方法不一致。 (1). 在w…

java mediator_java—mediator中介模式

中介者模式是由GoF提出的23种软件设计模式的一种。Mediator模式是行为模式之一&#xff0c;Mediator模式定义:用一个中介者对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立的改变他们之间的交互。适用性…

第五节:泛型(泛型类、接口、方法、委托、泛型约束、泛型缓存、逆变和协变)

一. 泛型诞生的背景 在介绍背景之前&#xff0c;先来看一个案例&#xff0c;要求&#xff1a;分别输出实体model1、model2、model3的id和name值,这三个实体有相同的属性名字id和name。 1 public class myUtils2 {3 //要求&#xff1a;分别输出实体model1、model2、…

第六节:反射(几种写法、好处和弊端、利用反射实现IOC)

一. 加载dll,读取相关信息 1. 加载程序集的三种方式 调用Assembly类下的三个方法&#xff1a;Load、LoadFile、LoadFrom。 1       //1.1 Load方法&#xff1a;动态默认加载当前路径下的(bin)下的dll文件,不需要后缀 2 Assembly assembly Assembly.Load(&…

第七节:语法总结(1)(自动属性、out参数、对象初始化器、var和dynamic等)

一. 语法糖简介 语法糖也译为糖衣语法&#xff0c;是由英国计算机科学家彼得约翰兰达&#xff08;Peter J. Landin&#xff09;发明的一个术语&#xff0c;指计算机语言中添加的某种语法&#xff0c;这种语法对语言的功能并没有影响&#xff0c;但是更方便程序员使用。通常来说…

java不用插件播放媒体文件_java servlet不用插件上传文件:

展开全部import java.net.*;import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;public class SaveFileServlet extends HttpServlet{FileWriter savefile;String filename null;String value null;/*** Handles a POST request*/publ…

第八节:语法总结(2)(匿名类、匿名方法、扩展方法)

一. 匿名类 1. 传统的方式给类赋值&#xff0c;需要先建一个实体类→实例化→赋值&#xff0c;步骤很繁琐&#xff0c;在.Net 3.0时代&#xff0c;微软引入匿名类的概念&#xff0c;简化了代码编写&#xff0c;提高了开发效率。 匿名类的声明语法&#xff1a; var objnew {字段…

java 里面matches什么意思_Java Regex中的matches()和find()之间的区别

如果完整string匹配&#xff0c; matches()将只返回true。 find()会尝试find匹配正则expression式的子string中的下一个匹配项。 注意强调“下一个”。 这意味着&#xff0c;多次调用find()的结果可能不一样。 另外&#xff0c;通过使用find()你可以调用start()来返回子string匹…

第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)

一. 委托的发展历史和基本用法 说起委托&#xff0c;每个人可能都会对他有不同的理解&#xff0c;结合实战中委托的使用&#xff0c;我对其理解是&#xff1a;委托和类一样&#xff0c;是用户的一个自定义类型&#xff0c;委托可以有参数、有返回值&#xff0c;委托的关键字是d…

java为什么不使用odbc_java jdbc和odbc的区别是什么?jdbc和odbc的关系是怎样的?

对于jdbc和odbc你都了解多少呢?今天要给大家讲到的就是jdbc和odbc之间的内容&#xff0c;一起来了解一下jdbc和odbc的区别以及关系是怎样的吧!下面先来给大家介绍一下jdbc和odbc之间的区别。总的来说&#xff0c;jdbc和odbc的区别可以划分成三大部分&#xff0c;一起来看看。一…

第十节:委托和事件(2)(泛型委托、Func和Action、事件及与委托的比较)

一. 泛型委托 所谓的泛型委托&#xff0c;即自定义委托的参数可以用泛型约束&#xff0c;同时内置委托Func和Action本身就是泛型委托。 将上一个章节中的Calculator类中的方法用自定义泛型委托重新实现一下。 1 public class Calculator22 {3 //传统解决方案一&am…

java中sql之count_按SQL Server中的count()子句分组

我正在尝试编写一个SQL查询&#xff0c;它将返回聚合值列表;但是&#xff0c;我想通过其中一个聚合值(计数)对查询进行分组&#xff1a;select t.Field1, count(distinct(t.Field2), SUM(t.Value1)from MyTable tgroup by t.Field1, count(t.Field2)我已经尝试将计数放入子查询…

java+sm4+加密算法_SM4加密算法实现Java和C#相互加密解密

https://www.cnblogs.com/miaoziblog/p/9040473.html近期由于项目需要使用SM4对数据进行加密&#xff0c;然后传给Java后台&#xff0c;Java后台使用的也是SM4的加密算法但是就是解密不正确&#xff0c;经过一步步调试发现Java中好多数据类型与C#的相同的数据类型是存在不同的比…

用java设计节拍器_具有高速的Java节拍器

关于Thread.sleep()不可靠的答案是正确的&#xff1a;你不能指望它完全返回你指定的时间.事实上,我很惊讶你的节拍器可以使用,特别是当你的系统负载不足时.阅读Thread.sleep()的文档以获取更多详细信息.关于MIDI的Max Beikirch的答案是一个很好的建议&#xff1a;MIDI处理时机非…

java隐藏密钥_java – 在Android中隐藏密钥库密码的最佳方法是什么?

我是Android开发和实现SSLSockets的新手.在做了一些挖掘后,我能够设置一个正常工作的简单服务器/客户端.我认为实现可以使用一些工作,并且难以将密码加载到密钥库而不用纯文本.这是客户端的一些代码.如您所见,我将密码硬编码到本地var中.是否有更好的方法加载密钥库密码,所以我…

DotNet进阶系列

一. 回顾历史 回顾个人发展历程&#xff0c;自2012年初次接触开发至今(2018年)已经有六个年头&#xff0c;这期间陆陆续续学习并掌握了不少技术&#xff0c;C#语言、ORM框架、多线程技术、设计模式、前端技术、MVC、MVVM框架思想等等&#xff0c;每种技术随着多次使用&#xff…