浅谈RxJava处理业务异常的几种方式

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

关于异常

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

运行时异常:

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

检查性异常:

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

处理业务异常

业务异常:

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

1. 重试

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

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

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

/** * 获取内容 * @param fragment * @param param * @param cacheKey * @return */ public Maybe<ContentModel> 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).<ContentModel>toLifecycleTransformer()) .compose(RxUtils.<ContentModel>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<Flowable<? extends Throwable>, 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<Throwable, Publisher<?>>() { @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).<VMWModel>toLifecycleTransformer()) .subscribeOn(Schedulers.io()) .onErrorReturn(new Function<Throwable, VMWModel>() { @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<T> : DisposableMaybeObserver<T>() { 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.<ContentModel>maybeToMain()) .doFinally(new Action() { @Override public void run() throws Exception { refreshlayout.finishRefresh(); } }) .subscribe(new BaseMaybeObserver<ContentModel>(){ @Override public void onMaybeSuccess(ContentModel data) { adpter.addDataToFront(data); } });

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

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

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

自定义异常->网络异常->服务器异常->内部程序异常->未知异常 除了以上自定义异常之外,此库包含其它异常分发,默认适应场景为:Rx+Json 自定义异常使用请调用,ExceptionParseMgr类的addCustomerParser方法添加业务异常

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

总结

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

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

时间: 2024-08-03 10:16:02

浅谈RxJava处理业务异常的几种方式的相关文章

浅谈PHP发送HTTP请求的几种方式

PHP 开发中我们常用 cURL 方式封装 HTTP 请求,什么是 cURL? cURL 是一个用来传输数据的工具,支持多种协议,如在 Linux 下用 curl 命令行可以发送各种 HTTP 请求.PHP 的 cURL 是一个底层的库,它能根据不同协议跟各种服务器通讯,HTTP 协议是其中一种. 现代化的 PHP 开发框架中经常会用到一个包,叫做 GuzzleHttp,它是一个 HTTP 客户端,也可以用来发送各种 HTTP 请求,那么它的实现原理是什么,与 cURL 有何不同呢? Does

浅谈java异常处理(父子异常的处理)_java

我当初学java异常处理的时候,对于父子异常的处理,我记得几句话"子类方法只能抛出父类方法所抛出的异常或者是其子异常,子类构造器必须要抛出父类构造器的异常或者其父异常".那个时候还不知道子类方法为什么要这样子抛出异常,后来通过学习<Thinking in Java>,我才明白其中的道理,现在我再来温习一下. 一.子类方法只能抛出父类方法的异常或者是其子异常 对于这种限制,主要是因为子类在做向上转型的时候,不能正确地捕获异常 package thinkinginjava; p

java异常-Java如何处理异常有几种方式,如何处理的

问题描述 Java如何处理异常有几种方式,如何处理的 我是新手,求各位前辈,大牛教下我,万分感谢!希望能给我一些详细解说,在此谢谢了 解决方案 package com.eric.exception; public class RethrowException { public static void main(String[] args) { try { // g0(); // g1(); // g2(); // g3(); g6(); } catch (Exception e) {......

浅谈Java中的异常

  作为一个面向对象编程的程序员对于 下面的一句一定非常熟悉: 复制代码 try { // 代码块 } catch(Exception e) { // 异常处理 } finally { // 清理工作 } 复制代码 就是面向对象中最最常见的异常处理程序,而且甚至我们会莫名其妙的被编译器要求加上这个模块,甚至我们自己也不知道捕捉到异常该怎么处理... 为什么要有异常 其实这个问题不用多说,程序员都知道,简单总结为一句话:就是为了增强程序健壮性呗,比如下面的代码: Class Denominator

浅谈网络推广目前常用的三种方法

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 网络推广让我们的宣传更为迅速,更为简便,更易让顾客找到,我们在推广中所付出的努力也会更容易的得到回报,就这些方面的综合考虑,我们首先就是要把我们的网站或者产品,以最快的速度传达到消费者的眼前,了解消费者的心里需求,正所谓急顾客之所急,想顾客之所想,那么为了实现这些理想,我们有哪些方法可以操作呢?下面A5站长网SEO诊断团队(http://se

浅谈RxJava与2.0的新特性

简介 说起 RxJava ,相信诸多 Android 开发者都不会陌生.作为一个知名的响应式编程库,从前年开始逐渐变得火热,从小众到被众多 Android 开发者们广泛引入与流传,其在 GitHub 的 仓库 截止笔者写这篇文章时,已经有16400+个 star .甚至有一些大牛专门为 Android 写了 RxJava 的适配库,如 RxAndroid RxBinding RxLifecycle 为什么 RxJava 如此受到 Android 开发者们的欢迎.我想不外乎两个原因. 1. 异步

浅谈 RxJava 与 2.0 的新特性

简介 说起 RxJava ,相信诸多 Android 开发者都不会陌生.作为一个知名的响应式编程库,从前年开始逐渐变得火热,从小众到被众多 Android 开发者们广泛引入与流传,其在 GitHub 的仓库截止笔者写这篇文章时,已经有16400+个 star .甚至有一些大牛专门为 Android 写了 RxJava 的适配库,如 RxAndroid RxBinding RxLifecycle 为什么 RxJava 如此受到 Android 开发者们的欢迎.我想不外乎两个原因. 1. 异步 2.

浅谈工作中使用过的几种数据库

几年的工作下来,也使用过好几种数据库,准确点来说是"数据库管理系统",有关系型数据库,也有NoSQL. 关系型数据库: 1.MySQL:开源,性能高,成本低,可靠性高(这些特性往往让他成为很多公司和项目的首选数据库),单拿大规模网站应用来说,我们熟知的如维基百科,Google,和Facebook都是采用的MySQL.但是目前Oracle对MySQL的收购也许会让我们对MySQL免费应用的前景产生一些担忧. 我的使用体验:MySQL安装很方便,几乎在下一步下一步过程中就完成了.而且针对M

浅谈网络最基础的几个赚钱方式

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 首先声明,老鸟就没有必要看我这篇文章了都是谈一些最基本的话题,些这篇文章的目的是突然回想起自己初进入互联网之际在这个圈子里没有朋友没有熟人常常会在百度上搜"网络赚钱方法","网络真的能赚钱吗?"等等之类的词,希望这篇文章能给那些还在站长圈外而又想进入互联网的朋友们带来一点点的帮助,对站长这个行业有一个最