本文所涉及DEMO已上傳至https://github.com/LegendaryMystic/HYMVP
本人小白一個,文章廢話較多寞奸,如果你覺得talk is cheap,喜歡直接 read the fuck source code纠俭,可跳過直接前往
碼字不易妻熊,如果代碼能夠幫到你,望不吝給個鼓勵的star趁桃,感謝辽话!
本文承接上文RXJava2+Retrofit2+MVP+RXLifecycle+EventBus+...之你可能需要的那些套路(一),我們接著講RxJava+Retrofit結(jié)合MVP架構(gòu)模式在實(shí)際項(xiàng)目中你可能需要的一些基本的套路肄鸽。
套路四:異常、錯誤或解密統(tǒng)一處理
剛開始使用RxJava進(jìn)行網(wǎng)絡(luò)請求時油啤,我們常用onNext
作為請求響應(yīng)的回調(diào)典徘,onError
異常回調(diào)
public interface Observer<T> {
/**
* Provides the Observer with the means of cancelling (disposing) the
* connection (channel) with the Observable in both
* synchronous (from within {@link #onNext(Object)}) and asynchronous manner.
* @param d the Disposable instance whose {@link Disposable#dispose()} can
* be called anytime to cancel the connection
* @since 2.0
*/
void onSubscribe(@NonNull Disposable d);
/**
* Provides the Observer with a new item to observe.
* <p>
* The {@link Observable} may call this method 0 or more times.
* <p>
* The {@code Observable} will not call this method again after it calls either {@link #onComplete} or
* {@link #onError}.
*
* @param t
* the item emitted by the Observable
*/
void onNext(@NonNull T t);
/**
* Notifies the Observer that the {@link Observable} has experienced an error condition.
* <p>
* If the {@link Observable} calls this method, it will not thereafter call {@link #onNext} or
* {@link #onComplete}.
*
* @param e
* the exception encountered by the Observable
*/
void onError(@NonNull Throwable e);
/**
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
* <p>
* The {@link Observable} will not call this method if it calls {@link #onError}.
*/
void onComplete();
}
然而益咬,值得一提的是逮诲,這里onError
回調(diào)給我們的是一個Throwable
,面對這個Throwable
對象,或許我們只能log它的message幽告,而且可能反復(fù)的寫梅鹦。。這個時候就需要封裝了冗锁。
我們希望,像大多數(shù)網(wǎng)絡(luò)請求框架一樣齐唆,簡單的兩個回調(diào)函數(shù):
/**
* 請求成功
*
* @param response 服務(wù)器返回的數(shù)據(jù)
*/
public abstract void onSuccess(T response);
/**
* 服務(wù)器返回數(shù)據(jù),但code不在約定成功范圍內(nèi)
*
* @param msg 服務(wù)器返回的數(shù)據(jù)
*/
public abstract void onFailure(String msg);
請求成功冻河,返回給我們需要的數(shù)據(jù)展示到UI界面箍邮,請求失敗(比如參數(shù)錯誤等請求邏輯錯誤)芋绸,返回給用戶一條失敗提示媒殉,而對于請求異常(如網(wǎng)絡(luò)異常,數(shù)據(jù)解析失敗..等請求異常)摔敛,我們只需在內(nèi)部統(tǒng)一log記錄便于debug。
一般的全封,服務(wù)器返回給我們的數(shù)據(jù)可能是:
{
"code": 200,
"msg": "成功",
"data": {}
}
對應(yīng)實(shí)體類:
/* http響應(yīng)參數(shù)實(shí)體類
* 通過Gson解析屬性名稱需要與服務(wù)器返回字段對應(yīng),或者使用注解@SerializedName
* /
public class BaseResponse<T>{
private int code;
private String msg;
private T data;
/**
* 是否成功(這里約定200)
*
* @return
*/
public boolean isSuccess() {
return code == 200 ? true : false;
}
我們與后臺約定狀態(tài)碼符合約定規(guī)則時為請求成功马昙,正常情況下如果某一個http請求沒有發(fā)生異常,或者網(wǎng)絡(luò)錯誤刹悴,就會走onNext回調(diào)行楞,現(xiàn)在我們要讓code
不等于200的響應(yīng)進(jìn)入失敗回調(diào),
自定義一個ResultException
用于捕獲服務(wù)器約定的錯誤類型
public class ResultException extends RuntimeException{
private int code;
private String message;
public ResultException(int code, String message){
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
自定義一個Gson響應(yīng)體轉(zhuǎn)換類MyGsonResponseBodyConverter
public class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody,T> {
private final Gson gson;
private final Type type;
public MyGsonResponseBodyConverter(Gson gson, Type type){
this.gson = gson;
this.type = type;
}
@Override
public T convert(@NonNull ResponseBody value) throws IOException {
String response = value.string();
BaseResponse<T> result = gson.fromJson(response, BaseResponse.class);
if (result.isSuccess()){
//對于請求數(shù)據(jù)加密的 可以在這里做解密操作
return gson.fromJson(jsonStr,type);
}else {
//拋一個自定義ResultException 傳入失敗時候的狀態(tài)碼土匀,和信息
throw new ResultException(result.getCode(),result.getMsg());
}
}
}
拷貝一份GsonConverterFactory
子房,將GsonResponseBodyConverter
替換為前面我們自定義的MyGsonResponseBodyConverter
/**
* A {@linkplain Converter.Factory converter} which uses Gson for JSON.
* <p>
* Because Gson is so flexible in the types it supports, this converter assumes that it can handle
* all types. If you are mixing JSON serialization with something else (such as protocol buffers),
* you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
* last to allow the other converters a chance to see their types.
*/
public final class MyGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static MyGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static MyGsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new MyGsonConverterFactory(gson);
}
private final Gson gson;
private MyGsonConverterFactory(Gson gson) {
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
//return new GsonResponseBodyConverter<>(gson, adapter);
//返回我們自定義的Gson響應(yīng)體變換器
return new MyGsonResponseBodyConverter<>(gson, type);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
然后在構(gòu)建Retrofit
時注冊這個自定義的MyResponseConverterFactory
retrofit = new Retrofit.Builder()
.baseUrl(ApiService.BASE_URL)
.client(client)
//然后將下面的GsonConverterFactory.create()替換成我們自定義的MyResponseConverterFactory.create()
.addConverterFactory(MyResponseConverterFactory.create())
// .addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
到此為止與后臺服務(wù)器約定的錯誤類型,拋出到onError
回調(diào)中就完成了就轧,舉一反三证杭,對于請求返回Json數(shù)據(jù)加密的,以及返回Json數(shù)據(jù) 格式不固定的 比如:
{
"result":"結(jié)果代號妒御,0表示成功",
"msg":"成功返回時是消息數(shù)據(jù)列表解愤,失敗時是異常消息文本"
}
這里msg究竟應(yīng)該定義為String,還是一個List呢乎莉?
等等都可以在Gson響應(yīng)體轉(zhuǎn)換類GsonResponseBodyConverter中做相應(yīng)的解密或相關(guān)判斷轉(zhuǎn)換處理送讲,這里我就不一一列舉了奸笤。
對于請求異常,為了方便調(diào)試哼鬓,自定義一個ApiException
监右,對Throwable
做具體的處理
public class ApiException extends Exception {
//對應(yīng)HTTP的狀態(tài)碼
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 int code;
private String message;
public ApiException(Throwable throwable, int code) {
super(throwable);
this.code = code;
this.message = throwable.getMessage();
}
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
public static ApiException handleException(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:
//權(quán)限錯誤,需要實(shí)現(xiàn)重新登錄操作
// onPermissionError(ex);
break;
case NOT_FOUND:
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
ex.message = "默認(rèn)網(wǎng)絡(luò)異常"; //均視為網(wǎng)絡(luò)錯誤
break;
}
return ex;
} else if (e instanceof SocketTimeoutException) {
ex = new ApiException(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)連接超時异希,請檢查您的網(wǎng)絡(luò)狀態(tài)健盒,稍后重試!";
return ex;
} else if (e instanceof ConnectException) {
ex = new ApiException(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)連接異常宠互,請檢查您的網(wǎng)絡(luò)狀態(tài)味榛,稍后重試!";
return ex;
} else if (e instanceof ConnectTimeoutException) {
ex = new ApiException(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)連接超時予跌,請檢查您的網(wǎng)絡(luò)狀態(tài)搏色,稍后重試!";
return ex;
} else if (e instanceof UnknownHostException) {
ex = new ApiException(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)連接異常券册,請檢查您的網(wǎng)絡(luò)狀態(tài)频轿,稍后重試!";
return ex;
} else if (e instanceof NullPointerException) {
ex = new ApiException(e, ERROR.NULL_POINTER_EXCEPTION);
ex.message = "空指針異常";
return ex;
} else if (e instanceof javax.net.ssl.SSLHandshakeException) {
ex = new ApiException(e, ERROR.SSL_ERROR);
ex.message = "證書驗(yàn)證失敗";
return ex;
} else if (e instanceof ClassCastException) {
ex = new ApiException(e, ERROR.CAST_ERROR);
ex.message = "類型轉(zhuǎn)換錯誤";
return ex;
} else if (e instanceof JsonParseException
|| e instanceof JSONException
// || e instanceof JsonSyntaxException
|| e instanceof JsonSerializer
|| e instanceof NotSerializableException
|| e instanceof ParseException) {
ex = new ApiException(e, ERROR.PARSE_ERROR);
ex.message = "解析錯誤";
return ex;
} else if (e instanceof IllegalStateException) {
ex = new ApiException(e, ERROR.ILLEGAL_STATE_ERROR);
ex.message = e.getMessage();
return ex;
} else {
ex = new ApiException(e, ERROR.UNKNOWN);
ex.message = "未知錯誤";
return ex;
}
}
/**
* 約定異常
*/
public static class ERROR {
/**
* 未知錯誤
*/
public static final int UNKNOWN = 1000;
/**
* 連接超時
*/
public static final int TIMEOUT_ERROR = 1001;
/**
* 空指針錯誤
*/
public static final int NULL_POINTER_EXCEPTION = 1002;
/**
* 證書出錯
*/
public static final int SSL_ERROR = 1003;
/**
* 類轉(zhuǎn)換錯誤
*/
public static final int CAST_ERROR = 1004;
/**
* 解析錯誤
*/
public static final int PARSE_ERROR = 1005;
/**
* 非法數(shù)據(jù)異常
*/
public static final int ILLEGAL_STATE_ERROR = 1006;
}
最后烁焙,自定義一個Observer抽象類對請求回調(diào)做具體的處理
public abstract class BaseObserver<T> implements Observer<T> {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(T response) {
onSuccess(response);
}
@Override
public void onError(Throwable e) {
//拋出的與服務(wù)器約定的錯誤類型
if (e instanceof ResultException){
onFailure(e.getMessage());
}else {
String error = ApiException.handleException(e).getMessage();
_onError(error);
}
}
@Override
public void onComplete() {
}
/**
* 請求成功
*
* @param response 服務(wù)器返回的數(shù)據(jù)
*/
public abstract void onSuccess(T response);
/**
* 服務(wù)器返回數(shù)據(jù)航邢,但code不在約定成功范圍內(nèi)
*
* @param msg 服務(wù)器返回的數(shù)據(jù)
*/
public abstract void onFailure(String msg);
// public abstract void onError(String errorMsg);
private void _onSuccess(T responce){
}
private void _onFailure(String msg) {
if (TextUtils.isEmpty(msg)) {
// ToastUtils.show(R.string.response_return_error);
} else {
// ToastUtils.show(msg);
}
}
private void _onError(String err ){
Log.e("APIException",err);
}
}
使用的時候在presenter里subscribe自定義的BaseObserver即可
mModel.getSurvey(did)
.compose(RxTransformer.transformWithLoading(mView))
.subscribe(new BaseObserver<Survey>() {
@Override
public void onSuccess(Survey response) {
mView.onGetSurvey(response);
}
@Override
public void onFailure(String msg) {
}
});
廢話有點(diǎn)多了,詳情請見源代碼HYMVP由于篇幅問題骄蝇,后續(xù)諸多套路膳殷,請聽下回分解。