Retrofit請求資料對錯誤以及網路異常的處理
阿新 • • 發佈:2018-12-31
異常處理
Retrofit本身會丟擲HttpException,Gson解析會丟擲解析異常,
此外我們還應該處理與伺服器約定好的“異常”,即上一篇提到的返回資料中result欄位值不會0的情況
這裡要先解決一個問題,就是Gson構建的物件,通過註解定義key名,以變數的型別定value的型別,
但如果同樣的key在不同情況下屬於不同的資料型別,就會出問題。
假如伺服器返回格式是
{
"result":"結果代號,0表示成功",
"msg":"成功返回時是訊息資料列表,失敗時是異常訊息文字"
}
麼msg究竟應該定義為String,還是一個List呢
我找到的解決方法就是:
註冊一個自定義的轉換類GsonResponseBodyConverter
先用一個只含result變數的Model類去解析獲得result值
如果失敗,則用msg是String的Model類再去解析msg值,然後組成一個ResultException
如果成功,則按照原本的Model類解析
程式碼如下:
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 {
}
}
}
這個類用於捕獲伺服器約定的錯誤型別
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替換為前面我們自定義的
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時註冊這個工廠類
Retrofit = new Retrofit.Builder()
.baseUrl(API_SERVER + "/")
//註冊自定義的工廠類
.addConverterFactory(ResponseConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(mOkHttpClient)
.build();
這樣就完成了Retrofit也可以丟擲伺服器約定異常
然後就是具體的處理:
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,足夠呼叫者需要
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 + ")";
}
}