问题

本文主要介绍了在 Retrofit + ReJava 环境下更优雅的解决了以下两个问题:


1. 将接口业务上的失败重定向到 onError(Throwable e) 输出;


2. 业务成功时分离出 data 数据由 onNext(T t) 输出;


场景

一般来说,一个友好的接口返回 json 应该是这样的:

1
2
3
4
5
6
7
8
9
{
"code":200,
"codeMsg":"查询成功!",
"data":{
"city":"北京",
"temperature":"8℃~20℃",
"weather":"晴转霾"
}
}

或者这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"code":200,
"codeMsg":"查询成功!",
"data":[
{
"city":"北京",
"temperature":"8℃~20℃",
"weather":"晴转霾"
},
{
"city":"南京",
"temperature":"12℃~21℃",
"weather":"晴"
}
]
}

如果约定好,code == 2xx,表示业务成功。其他表示业务失败,codeMsg 更详细地描述业务状态信息。例如:

1
2
3
4
{
"code":"400",
"codeMsg":"未知的城市"
}

实现

要知道,利用 Retrofit + Gson (或者其他 Converter ) 可以将返回的 Json 的转换为对象的,那么可以定义以下返回通用类 Model :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* CommonResponse - 通用类
* 泛型 T 可以为 List<object> , 也可以是 Object , 也可以为空。
*/
public class CommonResponse<T> {
private int code;
private String codeMsg;
private T data;
public int getCode() {
return code;
}
public String getCodeMsg() {
return codeMsg;
}
public T getData() {
return data;
}
/**
* 根据 code 码判断业务状态
* code == 2xx,表示业务成功。其他表示业务失败,codeMsg 进一步描述业务状态。
*/
public boolean isSuccess() {
return code / 100 == 2;
}
}

为了解决上面提出的两个问题,这里可以使用 RxJava 中的 lift() 来自定义操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 这里为了示例,简化了Retrofit Service、Api 创建的过程。
*/
JKApi mApi = JKService.createJKService();
mApi.getWeatherForCity("北京").lift(new Observable.Operator<T, CommonResponse<T>>() {
@Override
public Subscriber<? super CommonResponse<T>> call(final Subscriber<? super T> subscriber) {
return new Subscriber<CommonResponse<T>>() {
@Override
public void onCompleted() {
subscriber.onCompleted();
}
@Override
public void onError(Throwable e) {
subscriber.onError(e);
}
@Override
public void onNext(CommonResponse<T> tCommonResponse) {
if (tCommonResponse.isSuccess())
subscriber.onNext(tCommonResponse.getData());
else
subscriber.onError(new Throwable(tCommonResponse.getCodeMsg()));
}
};
}
});

上面的 lift() 操作想要对每一个符合这样标准的接口都通用,并且顺便对线程指定进行封装。可以 implements Observable.Transformer<?, ?>:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class RedirectResponseTransformer<T> implements Observable.Transformer<CommonResponse<T>, T> {
@Override
public Observable<T> call(
Observable<CommonResponse<T>> CommonResponseObservable) {
return CommonResponseObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.lift(new Observable.Operator<T, CommonResponse<T>>() {
@Override
public Subscriber<?super CommonResponse<T>> call(
final Subscriber<?super T> subscriber) {
return new Subscriber<CommonResponse<T>>() {
@Override
public void onCompleted() {
subscriber.onCompleted();
}
@Override
public void onError(Throwable e) {
subscriber.onError(e);
}
@Override
public void onNext(
CommonResponse<T> tCommonResponse) {
if (tCommonResponse.isSuccess()) {
subscriber.onNext(tCommonResponse.getData());
} else {
subscriber.onError(new Throwable(
tCommonResponse.getCodeMsg()));
}
}
};
}
});
}
}

最后我们使用 Observable.compose(Transformer transformer) 方法将以上的封装加入到 Rx-Chain 中去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 这里为了示例,简化了Retrofit Service、Api 创建的过程。
*/
JKApi mApi = JKService.createJKService();
mApi.getWeatherForCity("北京")
.compose(new RedirectResponseTransformer<WeatherModel>())
.subscribe(new Subscriber<WeatherModel>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
showShortToast(e.toString());
}
@Override
public void onNext(WeatherModel weatherModel) {
// do something like refresh ui
}
});

到这里,针对上文提出的两个问题已经做了封装。其实利用 RxJava 强大的运算符还可以做很多封装。例如:针对特定异常显示Toast、onComplete() 回调中 dismiss loadingDialog 等。以后将不断完善。