Retrofit+RxJava实战日志(3)-网络异常处理

上一篇文章写了Retrofit和RxJava的基本使用,最后的subscriber并没有写具体实现
成功的返回通常对应了各种不同的处理,而异常返回通常有着统一的处理
因此这一篇单独讲讲异常处理

Retrofit本身会抛出HttpException,Gson解析会抛出解析异常,
此外我们还应该处理与服务器约定好的“异常”,即上一篇提到的返回数据中result字段值不会0的情况

这里要先解决一个问题,就是Gson构建的对象,通过注解定义key名,以变量的类型定value的类型,
但如果同样的key在不同情况下属于不同的数据类型,就会出问题。
比如上一篇文章提及,服务器返回格式是

1
2
3
4
{
"result":"结果代号,0表示成功",
"msg":"成功返回时是消息数据列表,失败时是异常消息文本"
}

那么msg究竟应该定义为String,还是一个List呢

我找到的解决方法就是:
注册一个自定义的转换类GsonResponseBodyConverter
先用一个只含result变量的Model类去解析获得result值
如果失败,则用msg是String的Model类再去解析msg值,然后组成一个ResultException
如果成功,则按照原本的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
class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final Type type;
GsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
@Override
public T convert(ResponseBody value) throws IOException{
String response = value.string();
try {
Log.d("Network", "response>>" + response);
//ResultResponse 只解析result字段
ResultResponse resultResponse = gson.fromJson(response, ResultResponse.class);
if (resultResponse.getResult() == 0){
//result==0表示成功返回,继续用本来的Model类解析
return gson.fromJson(response, type);
} else {
//ErrResponse 将msg解析为异常消息文本
ErrResponse errResponse = gson.fromJson(response, ErrResponse.class);
throw new ResultException(resultResponse.getResult(), errResponse.getMsg());
}
} finally {
}
}
}

这个类用于捕获服务器约定的错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ResultException extends RuntimeException {
private int errCode = 0;
public ResultException(int errCode, String msg) {
super(msg);
this.errCode = errCode;
}
public int getErrCode() {
return errCode;
}
}

拷贝原生的ResponseConverterFactory,将GsonResponseBodyConverter替换为前面我们自定义的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ResponseConverterFactory extends Converter.Factory {
...
...
@Override
public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
return new GsonResponseBodyConverter<>(gson, type);
}
@Override
public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
return new GsonRequestBodyConverter<>(gson, type);
}
}

然后在构建Retrofit时注册这个工厂类

1
2
3
4
5
6
7
Retrofit = new Retrofit.Builder()
.baseUrl(API_SERVER + "/")
//注册自定义的工厂类
.addConverterFactory(ResponseConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(mOkHttpClient)
.build();

这样就完成了Retrofit也可以抛出服务器约定异常

然后就是具体的处理:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public abstract class AbsAPICallback<T> extends Subscriber<T> {
//对应HTTP的状态码
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int REQUEST_TIMEOUT = 408;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;
//出错提示
private final String networkMsg;
private final String parseMsg;
private final String unknownMsg;
protected AbsAPICallback(String networkMsg, String parseMsg, String unknownMsg) {
this.networkMsg = networkMsg;
this.parseMsg = parseMsg;
this.unknownMsg = unknownMsg;
}
@Override
public void onError(Throwable e) {
Throwable throwable = e;
//获取最根源的异常
while(throwable.getCause() != null){
e = throwable;
throwable = throwable.getCause();
}
ApiException ex;
if (e instanceof HttpException){ //HTTP错误
HttpException httpException = (HttpException) e;
ex = new ApiException(e, httpException.code());
switch(httpException.code()){
case UNAUTHORIZED:
case FORBIDDEN:
onPermissionError(ex); //权限错误,需要实现
break;
case NOT_FOUND:
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
ex.setDisplayMessage(networkMsg); //均视为网络错误
onError(ex);
break;
}
} else if (e instanceof ResultException){ //服务器返回的错误
ResultException resultException = (ResultException) e;
ex = new ApiException(resultException, resultException.getErrCode());
onResultError(ex);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){
ex = new ApiException(e, ApiException.PARSE_ERROR);
ex.setDisplayMessage(parseMsg); //均视为解析错误
onError(ex);
} else {
ex = new ApiException(e, ApiException.UNKNOWN);
ex.setDisplayMessage(unknownMsg); //未知错误
onError(ex);
}
}
/**
* 错误回调
*/
protected abstract void onError(ApiException ex);
/**
* 权限错误,需要实现重新登录操作
*/
protected abstract void onPermissionError(ApiException ex);
/**
* 服务器返回的错误
*/
protected abstract void onResultError(ApiException ex);
@Override
public void onCompleted() {
}
}

自定义ApiException,携带了异常代码和信息,以及根源Throwable,足够调用者需要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ApiException extends Exception {
private final int code;
private String displayMessage;
public static final int UNKNOWN = 1000;
public static final int PARSE_ERROR = 1001;
public ApiException(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
public int getCode() {
return code;
}
public String getDisplayMessage() {
return displayMessage;
}
public void setDisplayMessage(String msg) {
this.displayMessage = msg + "(code:" + code + ")";
}
}