美文网首页week.ioRx系列Rxjava
使用RxJava优雅的处理服务器返回异常

使用RxJava优雅的处理服务器返回异常

作者: YoKey | 来源:发表于2015-12-09 18:44 被阅读5381次

实际开发经常有这种情况,比如登录请求,返回来的并不会仅仅是User对象,而是被包装的RESTResult<User>对象,RESTResult对象里,包括请求返回的状态:失败还是成功,错误码,User对象等等。
如下:

public class RESTResult<T> {
    public static final int FAILURE = 0; // 失败
    public static final int SUCCESS = 1; // 成功

    private int status;  // 返回状态:0 失败   1 成功
    private HttpResponseCode code;  // 错误码
    private String message;  // 返回信息
    private T data;  // 包装的对象

    // 其他省略
    .... 

在使用Retrofit+RxJava的情况下,以登录为例,ApiService如下:

public interface ApiService {
    @FormUrlEncoded
    @POST(API + "account/login")
    Observable<RESTResult<User>> login(@Field("mobile") String mobile, @Field("code") String code);
}

我们对于登录返回的结果会这样处理:如果status为SUCCESS,则获取到User对象并进行后续操作;如果status为FAILURE,则需要根据code或者message提示用户相应的错误提示,并隐藏进度条对话框等操作。

下面有2种处理方式,方案1是我们没接触或刚接触Rx时会想到的方案,方案2里是比较优雅的处理方式 :)
方案1:

    _apiService.login(mobile, verifyCode)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnTerminate(() -> hideLoadingDialog())
            .subscribe(new Action1<RESTResult<User>>() {
                @Override
                public void call(RESTResult<User> userRESTResult) {
                    if (result.getStatus() == RESTResult.FAILURE) {
                        HttpResponseCode code = result.getCode();
                        // 根据不同code进行不同处理
                        ...
                        Toast.makeText(_context, result.getMessage(), Toast.LENGTH_SHORT).show();
                    } else {
                        User user = result.getData();
                        ...
                    }
                }
            }, new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    throwable.printStackTrace();
                    Toast.makeText(_context, "请求失败,请稍后重试", Toast.LENGTH_SHORT).show();
                }
            });

显然这个方案有太多缺点:
1、RxJava建议在subscribe的时候,观察者应该拿到的是加工完成的数据源,而不是未加工的数据源;
2、在服务器返回status=FAILURE的情况下,异常是在onNext里处理,这样并不合适;
3、判断RESTResult的部分更应该放在链式操作里。
我们需要炫酷的Rx操作符!

最佳体验:

方案2:使用Observable.error(e):

    _apiService.login(mobile, verifyCode)
            .// 略  显示提示框、切换线程
            .flatMap(result -> {
                if (result.getStatus() == RESTResult.FAILURE) {
                    HttpResponseCode code = result.getCode();
                    // 根据不同code进行不同处理
                    ...
                 
                    return Observable.error(new ServerException(result.getMessage()));
                }
                return Observable.just(result.getData());
            })
            .subscribe(new Action1<User>() {
                    @Override
                    public void call(User user) {
                       // user对象
                    }
              }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        throwable.printStackTrace();

                        if (e instanceof ServerException){
                           Toast.makeText(_context, e.getMessage(), Toast.LENGTH_SHORT).show();
                        } else{
                           Toast.makeText(_context, "请求失败,请稍后重试", Toast.LENGTH_SHORT).show();
                        }
                    }  
              });

flaMap操作符可以接收一个Observable的输出作为输入,同时输出另外一个Observable。在服务器返回status=FAILURE的情况下,返回Observable.error(Throwable exception),SUCCESS的话,just源数据为一个新的Observable返回。

Observable.error(Throwable exception):

Returns an Observable that invokes an Observer's onError method when the Observer subscribes to it.

该方法是返回一个Observable,当观察者订阅时,执行观察者的onError方法。

这个方案完美的解决了方案1、2的问题:
1、观察者拿到“加工”完成的数据源;
2、异常方面,不管是服务器异常还是其他异常情况,最终观察者都可以接收到异常数据源,并最终统一在onError里处理。
3、使用flatMap处理RESTResult部分的代码,可以放在任意线程处理

最后:

方案2通过合适的封装,都可达到代码简化的目的。
封装部分可以查看我这篇简书:RxJava简洁封装之道

通过RxJava的链式操作,结合恰当的操作符,不仅可以把正常的数据源发射给观察者,同时也可以将错误异常数据源发射给观察者,RxJava远比想象中的强大!

方案2是目前我发现的最合适方案,如果还有更好的解决方案,欢迎告诉我哈~

相关文章

网友评论

  • 人海中一只羊:楼主的思路很棒:在上游解析 REST result而不是留到发射后再处理
  • 97a238381280:赞,提供了新的思路
    但是楼主我想请问一下,如果是使用retrofit,可以直接继承Converter.Factory,在里面统一对接口返回的json数据做处理,状态码正确的话返回相应的data,状态码错误则抛出错误。。这样不是更简洁嘛?如果按照第二种方案每次调用都要写一遍flatmap来着。。求指导
    人海中一只羊:不同接口后台返回的数据的格式是有差异的,
    处理状态码的逻辑也是不同的.
    难道手动解析json比解析javaBean更简单??
  • 聪葱忙忘:_apiService.login(mobile, verifyCode)
    .// 略 显示提示框、切换线程
    .flatMap(result -> {
    这里想问下你_apiService.login(mobile, verifyCode)返回的是什么,为什么可以直接调用flatMap?
    jdsjlzx:@YoKey 是因为使用了RxJava,才返回了Observable<XXX>
    YoKey:@聪葱忙忘 这里用的是Retrofit,返回的是一个Observable<XXX>的结果~
  • 梦沉薇露:我怎么没看到方案3?
  • 无辜的小黄人:如果个别的请求数据返回没有data,只有code和msg怎么处理呢
    YoKey:@无辜的小黄人 额 会报错吗…… 后台格式不能规范的话 只有前台再专门处理一下了~
    印象中解析应该没问题
    无辜的小黄人:@YoKey 怎么说?现在只有msg和code的话会报错的 :disappointed_relieved:
    YoKey: @无辜的小黄人 data当作Object 这样可以保证正常工作 只是data是null而已
  • Euterpe:Retrofit 的 api service 接口 作者你是怎么 管理创建 对象的 一个项目有很多个 加上用上dagger2 是咋 处理 retrofit 生成 api service 接口对象??
    YoKey:@Euterpe 是的~
    Euterpe:@YoKey 就是在@model里面 提供所有service接口 对象??
    YoKey:@Euterpe 将Service作为单例,多个Service多个单例,需要哪个get哪个~ :smile:
  • 北方南山:服务端有这样两种返回的情况
    ···
    {
    code:"",
    msg:"",
    object:""
    }
    返回单独一个实体的时候
    {
    code:"",
    msg:"",
    list:""
    }
    返回list 一组数据
    {
    code:"",
    msg:"",
    list:""
    currentpage:"",
    totalpage:"",
    }
    分页返回
    ···
    这样的服务端数据返回设计,特别是前两个,是不是不很好呀?
    北方南山:@YoKey 那就是果然需要两个处理。
    YoKey:@北方南山 这样设计坑的~ 前端要根据不同接口,转成2个不同的VO ~
    建议object,list 都归为data,前端只要使用T data,就可以仅用一个VO接收不同接口了~
  • cf2684cf16b1:怎么只有方案1和方案2?
    cf2684cf16b1:@YoKey 好的,第二种很好用.谢谢
    YoKey:@一树1916 哦哦 之前还有2种,但是一个意义不大,一个是代码异常的处理 不是服务器相关的
    就删掉了 抱歉 文章那里还改掉 现在已改
  • 皮球二二:有启发,感谢
  • 47287ba8b36e:太巧了,我的想法跟楼主完全一样,最后实现出来自定义了一个subscriber公共类,里面onError处理了一些具体的类似转换错误提示,统一状态码处理如token过期,等公用错误处理逻辑,然后如果是server端具体的错误逻辑会封装成一个自定义Exception传递给抽象方法onServerError,方便逻辑层具体处理业务逻辑
    47287ba8b36e:@YoKey 赞
    YoKey:@AmosZhong 嗯嗯 :blush: 之前还发现一种处理方式,方案3,现在已经更新,可以看看
  • 天之大任:期待更多的rx文章
  • 天之大任:这个问题一直在思考,厉害,Rxjava操作符确实强大
  • 卓修武:Good article
  • HelloVass:棒,第三种方案能统一处理异常这点很好

本文标题:使用RxJava优雅的处理服务器返回异常

本文链接:https://www.haomeiwen.com/subject/waoehttx.html