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

转载  2017-11-09   作者:fengzhizi715   我要评论

本篇文章主要介绍了浅谈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

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

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

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

总结

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

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

相关文章

  • Android使用PullToRefresh完成ListView下拉刷新和左滑删除功能

    Android使用PullToRefresh完成ListView下拉刷新和左滑删除功能

    ListView下刷新刷功能相信从事Android开发的猿友们并不陌生,本文就带领一些刚入门android的朋友或者一起爱分享的朋友来简单的实现ListView的下拉刷新和左滑删除效果。感兴趣的朋友一起看看吧
    2016-11-11
  • 浅谈Android为RecyclerView增加监听以及数据混乱的小坑

    浅谈Android为RecyclerView增加监听以及数据混乱的小坑

    下面小编就为大家带来一篇浅谈Android为RecyclerView增加监听以及数据混乱的小坑。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • 分析Android多主题颜色的相关问题

    分析Android多主题颜色的相关问题

    这篇文章总结了在Android开发多主题颜色的时候会遇到的一些问题,然后给出解决方案,让大家可以解决问题,有需要的下面一起来看看吧。
    2016-08-08
  • Android View移动的3种方式总结

    Android View移动的3种方式总结

    这篇文章主要给大家介绍了Android View移动的三种方式,在介绍这三种方式之前先介绍了Android坐标系的定义规则以及View的一些位置参数。有需要的朋友们可以参考借鉴。
    2016-09-09
  • Android实现ImageView图片缩放和拖动

    Android实现ImageView图片缩放和拖动

    这篇文章主要为大家详细介绍了Android实现ImageView图片缩放和拖动的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Android系统永不锁屏永不休眠的方法

    Android系统永不锁屏永不休眠的方法

    在进行Android系统开发的时候,有些特定的情况需要设置系统永不锁屏,永不休眠.本篇文章给大家介绍Android 永不锁屏,开机不锁屏,删除设置中休眠时间选项,需要的朋友一起学习吧
    2016-07-07
  • 总结Android中MD风格相关控件

    总结Android中MD风格相关控件

    自Android5.0发布以来,谷歌推出全新的Material Desigen设计风格,时过一年多了,在国内也看到很多应用在慢慢适应MD设计风格。今天小编给大家总结下Android中MD风格相关控件的知识,有需要的可以参考学习。
    2016-08-08
  • Android中Volley框架下保持会话方法

    Android中Volley框架下保持会话方法

    这个是基于session的一个网络会话,手机app给服务器发送登陆请求的时候,服务器返回的网络response(networkRespone)的头(head)里面存放着你想要的sessionid。这篇文章主要介绍了Android中Volley框架下保持会话方法的相关资料,需要的朋友可以参考下
    2016-11-11
  • Android实现环形进度条代码

    Android实现环形进度条代码

    这篇文章主要为大家详细介绍了Android实现环形进度条的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Activity生命周期与启动模式图文解说

    Activity生命周期与启动模式图文解说

    这篇文章主要介绍了Activity生命周期与启动模式图文解说,内容比较详细,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11

最新评论