RxJava+Retrofit,在联网返回后如何先进行统一的判断?

最近在学习rxjava,首先用于联网部分。数据为json格式。 json格式固定为: { "ResultMessage":null, "R
关注者
168
被浏览
10668

10 个回答

更新:
新发现RxJava有个compose方法,可以不必在外部包裹一层,在之前回答基础上,可以采用以下方式调用:
mApiService.getUserInfo(params...).compose(ApiWrapper::warp)
        .subscribe(data -> {...}, error -> {...})
//以下是原回答

本回答在@twiceYuan的回答的基础上进行了改进
刚开始学着用RxJava,今天也遇到了同样的问题
之前的解决方案参考了@twiceYuan的回答
但我这边的需求要复杂些,除了做同楼主一样的处理,还需要接一些其它的处理(map, filter)什么的,这样的话每次新增一个api,都要在apiWrapper和ApiService里面添加两个几乎一样方法,而且要在wrapper的方法里copy一串一摸一样的处理代码,看起来像这样:
Observable<User> getUserInfo() {
    return mApi.getUserInfo
               .flatMap(RetroUtil::flatResult)
               .map(RetroUtil::mapResult)
               .map(RetroUtil::mapResult2)
               .flatMap(RetroUtil::flatResult2);

}

Observable<User> getUserInfo(String id) {
    return mApi.getUserInfo(id)
               .flatMap(RetroUtil::flatResult)
               .map(RetroUtil::mapResult)
               .map(RetroUtil::mapResult2)
               .flatMap(RetroUtil::flatResult2);

}

Observable<User> getUserInfo(String id, String token) {
    return mApi.getUserInfo(id, token)
               .flatMap(RetroUtil::flatResult)
               .map(RetroUtil::mapResult)
               .map(RetroUtil::mapResult2)
               .flatMap(RetroUtil::flatResult2);

}
这样产生了不少重复劳动,而且看起来不够优雅,经过改进,wrapper类精简到了对外只暴露一个wrap方法,针对不同的参数个数有几个类似的多态函数,代码如下:
public static <R, P1, P2, P3, P4> Observable<R> wrap(P1 p1, P2 p2, P3 p3, P4 p4, Func4<P1, P2, P3, P4, Observable<ResponseBean<R>>> func) {
        return wrap(func.call(p1, p2, p3, p4));
    }

    public static <R, P1, P2, P3> Observable<R> wrap(P1 p1, P2 p2, P3 p3, Func3<P1, P2, P3, Observable<ResponseBean<R>>> func) {
        return wrap(func.call(p1, p2, p3));
    }

    public static <R, P1, P2> Observable<R> wrap(P1 p1, P2 p2, Func2<P1, P2, Observable<ResponseBean<R>>> func) {
        return wrap(func.call(p1, p2));
    }

    public static <R, P1> Observable<R> wrap(P1 p1, Func1<P1, Observable<ResponseBean<R>>> func) {
        return wrap(func.call(p1));
    }

    public static <R> Observable<R> wrap(Func0<Observable<ResponseBean<R>>> func) {
        return wrap(func.call());
    }
    
    public static <R> Observable<R> wrap(Observable<ResponseBean<R>> result) {
        return result.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(RetroUtil::flatResult)
                .map(RetroUtil::mapResult)
                .map(RetroUtil::mapResult2)
                .flatMap(RetroUtil::flatResult2);
    }

这样在外部调用时,只需要:
ApiWrapper.wrap(param1, param2, mApiService::method)
        .subscribe(data -> {...}, error -> {...})

//或者
ApiWrapper.wrap(mApiService.getUserInfo(params...))
        .subscribe(data -> {...}, error -> {...})
这样新增api的时候,只需要在ApiService同往常一样添加一个方法,而不必在ApiWrapper里面copy代码
// UPDATE AT 2016/12/30
之前那种方式还是有很多问题的,现在项目中用了自我感觉更好的一种方式:
1. 使用 Interceptor 对结果进行拦截
2. 拦截后的结果进行预处理(只解析自己服务端定义的状态码、错误信息等),如果为错误信息,抛出定义的和业务逻辑的异常。
3. 调用接口的时候,直接对异常进行处理。同时也对没有处理的异常已经统一的默认处理定义(例如错误在没有 catch 的时候都直接 toast 提示,需要特殊处理的地方直接 catch 就好了)。
这样处理感觉更直观一下,下面是以前的答案内容,仅供参考。

我们也是刚开始在一个项目中用 Rx + Retrofit,这个问题应该是比较常见的,我们的处理方式可能不是最好的,把详细些的流程介绍一下吧。

1. 首先定义带泛型的返回结果,Retrofit API 的原生结果映射为这种形式:

class Result<T> {
    String ResultMessage;
    int ResultCode;
    T Data; 
}

2. 处理错误的方法和 @朱诗雄 前辈方法差不多,放到作为静态方法放到 RetroUtil 里,这里 ApiException 为自己定义的一个异常,放入错误码和错误信息(错误码不止一个):

static <T> Observable<T> flatResult(Result<T> result) {
    return Observable.create(subscriber -> {
            switch (result.ResultCode) {
                case Constants.SUCCESS_CODE:
                    subscriber.onNext(result.Data);
                    break;
                case Constants.ERROR_CODE:                     
                    subscriber.onError(new ApiException(result.ResultCode, result.ResultMessage);
                    break;
                default:
                    // ...
            }
            subscriber.onCompleted();
        }
    });
}

3. 在 API 包装类对于上述 Result<T> 格式的返回结果,统一调用 flatMap(RetroUtil::flatResult) 后的 API。这样每个 API 的返回结果就是 Observable<Data> 的形式并且在 errorHandler 中统一处理错误了。

// 接口方法
Observable<Result<User>> getUserInfo();

// 包装后的方法
Observable<User> getUserInfo() {
    return mApi.getUserInfo.flatMap(RetroUtil::flatResult);
}

// 调用时
apiWrapper.getUserInfo()
    .subscrible(user -> {
        // 处理正常逻辑
    }, error -> {
        // 处理错误逻辑和异常,这里封装时通常也会统一处理,
        // 提供一个默认的 Action1<Throwable> 参数,弹出
        // throwable 的 message 打印日志等
    });
为什么?
二维码
下载知乎客户端
与世界分享知识、经验和见解
二维码
下载知乎客户端
与世界分享知识、经验和见解