01.網(wǎng)絡(luò)請求異常分類
02.開發(fā)中注意問題
03.原始的處理方式
04.如何減少代碼耦合性
05.異常統(tǒng)一處理步驟
06.完成版代碼展示
好消息博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處,謝謝!
鏈接地址:github.com/yangchong21…
如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!
01.網(wǎng)絡(luò)請求異常分類網(wǎng)絡(luò)請求異常大概有哪些?
第一種:訪問接口異常,比如404,500等異常,出現(xiàn)這類異常,Retrofit會自動拋出異常。
第二種:解析數(shù)據(jù)異常,數(shù)據(jù)體發(fā)生變化可能會導(dǎo)致這個問題。
第三種:其他類型異常,比如服務(wù)器響應(yīng)超時異常,鏈接失敗異常,網(wǎng)絡(luò)未連接異常等等。
第四種:網(wǎng)絡(luò)請求成功,但是服務(wù)器定義了異常狀態(tài),比如token失效,參數(shù)傳遞錯誤,或者統(tǒng)一給提示(這個地方比較拗口,比如購物app,你購買n件商品請求接口成功,code為200,但是服務(wù)器發(fā)現(xiàn)沒有這么多商品,這個時候就會給你一個提示,然后客戶端拿到這個進行吐司)
02.開發(fā)中注意問題在獲取數(shù)據(jù)的流程中,訪問接口和解析數(shù)據(jù)時都有可能會出錯,我們可以通過攔截器在這兩層攔截錯誤。
1.在訪問接口時,我們不用設(shè)置攔截器,因為一旦出現(xiàn)錯誤,Retrofit會自動拋出異常。比如,常見請求異常404,500,503等等。為了方便后期排查問題,這個可以在debug環(huán)境下打印日志就可以。
2.在解析數(shù)據(jù)時,我們設(shè)置一個攔截器,判斷Result里面的code是否為成功,如果不成功,則要根據(jù)與服務(wù)器約定好的錯誤碼來拋出對應(yīng)的異常。比如,token失效后跳轉(zhuǎn)登錄頁面,禁用同賬號登陸多臺設(shè)備,缺少參數(shù),參數(shù)傳遞異常等等。
3.除此以外,為了我們要盡量避免在View層對錯誤進行判斷,處理,我們必須還要設(shè)置一個攔截器,攔截onError事件,然后使用ExceptionUtils,讓其根據(jù)錯誤類型來分別處理。
03.原始的處理方式最簡單的處理方式,直接對返回的throwable進行類型判斷處理
//請求,對throwable進行判斷
ServiceHelper.getInstance()
.getModelResult(param1, param2)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if(e instanceof HttpException){
//獲取對應(yīng)statusCode和Message
HttpException exception = (HttpException)e;
String message = exception.response().message();
int code = exception.response().code();
}else if(e instanceof SSLHandshakeException){
//接下來就是各種異常類型判斷...
}else if(e instanceof ...){
}...
}
@Override
public void onNext(Model model) {
if(model.getCode != CODE_SUCCESS){
int code = model.getCode();
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("重新登陸");
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情況");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司服務(wù)器返回的提示");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少參數(shù),用log記錄服務(wù)器提示");
break;
default:
ex.setDisplayMessage(message);
break;
}
}else{
//正常處理邏輯
}
}
});
04.如何減少代碼耦合性
為了不改變以前的代碼結(jié)構(gòu),那么如何做才能夠徹底解耦呢?一般情況下使用Retrofit網(wǎng)絡(luò)請求框架,會有回調(diào)方法,如下所示:
package retrofit2; public interface Callback{ void onResponse(Call var1, Response var2); void onFailure(Call var1, Throwable var2); }
不管以前代碼封裝與否,都希望一句代碼即可實現(xiàn)網(wǎng)絡(luò)請求攔截處理邏輯。那么這個時候,我是怎么處理的呢?
public class ResponseData {
private int code;
private String message;
private T t;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public T getT() {
return t;
}
}
new CallbacklogEntity>>(){
@Override
public void onResponse(CalllogEntity>> call,
ResponselogEntity>> response) {
int code = response.body().getCode();
String message = response.body().getMessage();
HomeBlogEntity t = response.body().getT();
if (code!= CODE_SUCCESS){
//網(wǎng)絡(luò)請求成功200,不過業(yè)務(wù)層執(zhí)行服務(wù)端制定的異常邏輯
ExceptionUtils.serviceException(code,message);
} else {
//網(wǎng)絡(luò)請求成功,業(yè)務(wù)邏輯正常處理
}
}
@Override
public void onFailure(Call call, Throwable throwable) {
ExceptionUtils.handleException(throwable);
}
};
05.異常統(tǒng)一處理步驟
第一步:定義請求接口網(wǎng)絡(luò)層失敗的狀態(tài)碼
/** * 對應(yīng)HTTP的狀態(tài)碼 */ private static final int BAD_REQUEST = 400; private static final int UNAUTHORIZED = 401; private static final int FORBIDDEN = 403; private static final int NOT_FOUND = 404; private static final int METHOD_NOT_ALLOWED = 405; private static final int REQUEST_TIMEOUT = 408; private static final int CONFLICT = 409; private static final int PRECONDITION_FAILED = 412; 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;
第二步,接口請求成功,業(yè)務(wù)層失敗,服務(wù)端定義異常狀態(tài)碼
比如,登錄過期,提醒用戶重新登錄;
比如,添加商品,但是服務(wù)端發(fā)現(xiàn)庫存不足,這個時候接口請求成功,服務(wù)端定義業(yè)務(wù)層失敗,服務(wù)端給出提示語,客戶端進行吐司
比如,請求接口,參數(shù)異?;蛘哳愋湾e誤,請求code為200成功狀態(tài),不過給出提示,這個時候客戶端用log打印服務(wù)端給出的提示語,方便快遞查找問題
比如,其他情況,接口請求成功,但是服務(wù)端定義業(yè)務(wù)層需要吐司服務(wù)端返回的對應(yīng)提示語
/**
* 服務(wù)器定義的狀態(tài)嗎
* 比如:登錄過期,提醒用戶重新登錄;
* 添加商品,但是服務(wù)端發(fā)現(xiàn)庫存不足,這個時候接口請求成功,服務(wù)端定義業(yè)務(wù)層失敗,服務(wù)端給出提示語,客戶端進行吐司
* 請求接口,參數(shù)異?;蛘哳愋湾e誤,請求code為200成功狀態(tài),不過給出提示,這個時候客戶端用log打印服務(wù)端給出的提示語,方便快遞查找問題
* 其他情況,接口請求成功,但是服務(wù)端定義業(yè)務(wù)層需要吐司服務(wù)端返回的對應(yīng)提示語
*/
/**
* 完全成功
*/
private static final int CODE_SUCCESS = 0;
/**
* Token 失效
*/
public static final int CODE_TOKEN_INVALID = 401;
/**
* 缺少參數(shù)
*/
public static final int CODE_NO_MISSING_PARAMETER = 400400;
/**
* 其他情況
*/
public static final int CODE_NO_OTHER = 403;
/**
* 統(tǒng)一提示
*/
public static final int CODE_SHOW_TOAST = 400000;
第三步,自定義Http層的異常和服務(wù)器定義的異常類
public class HttpException extends Exception {
private int code;
private String displayMessage;
public HttpException(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
public void setDisplayMessage(String displayMessage) {
this.displayMessage = displayMessage;
}
public String getDisplayMessage() {
return displayMessage;
}
public int getCode() {
return code;
}
}
public class ServerException extends RuntimeException {
public int code;
public String 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;
}
}
第四步,統(tǒng)一處理異常邏輯如下所示
/**
* 這個可以處理服務(wù)器請求成功,但是業(yè)務(wù)邏輯失敗,比如token失效需要重新登陸
* @param code 自定義的code碼
*/
public static void serviceException(int code , String content){
if (code != CODE_SUCCESS){
ServerException serverException = new ServerException();
serverException.setCode(code);
serverException.setMessage(content);
handleException(serverException);
}
}
/**
* 這個是處理網(wǎng)絡(luò)異常,也可以處理業(yè)務(wù)中的異常
* @param e e異常
*/
public static void handleException(Throwable e){
HttpException ex;
//HTTP錯誤 網(wǎng)絡(luò)請求異常 比如常見404 500之類的等
if (e instanceof retrofit2.HttpException){
retrofit2.HttpException httpException = (retrofit2.HttpException) e;
ex = new HttpException(e, ErrorCode.HTTP_ERROR);
switch(httpException.code()){
case BAD_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case REQUEST_TIMEOUT:
case CONFLICT:
case PRECONDITION_FAILED:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
//均視為網(wǎng)絡(luò)錯誤
default:
ex.setDisplayMessage("網(wǎng)絡(luò)錯誤"+httpException.code());
break;
}
} else if (e instanceof ServerException){
//服務(wù)器返回的錯誤
ServerException resultException = (ServerException) e;
int code = resultException.getCode();
String message = resultException.getMessage();
ex = new HttpException(resultException, ErrorCode.SERVER_ERROR);
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("token失效");
//下面這里可以統(tǒng)一處理跳轉(zhuǎn)登錄頁面的操作邏輯
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情況");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少參數(shù)");
break;
default:
ex.setDisplayMessage(message);
break;
}
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){
ex = new HttpException(e, ErrorCode.PARSE_ERROR);
//均視為解析錯誤
ex.setDisplayMessage("解析錯誤");
}else if(e instanceof ConnectException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//均視為網(wǎng)絡(luò)錯誤
ex.setDisplayMessage("連接失敗");
} else if(e instanceof java.net.UnknownHostException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//網(wǎng)絡(luò)未連接
ex.setDisplayMessage("網(wǎng)絡(luò)未連接");
} else if (e instanceof SocketTimeoutException) {
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//網(wǎng)絡(luò)未連接
ex.setDisplayMessage("服務(wù)器響應(yīng)超時");
} else {
ex = new HttpException(e, ErrorCode.UNKNOWN);
//未知錯誤
ex.setDisplayMessage("未知錯誤");
}
String displayMessage = ex.getDisplayMessage();
//這里直接吐司日志異常內(nèi)容,注意正式項目中一定要注意吐司合適的內(nèi)容
ToastUtils.showRoundRectToast(displayMessage);
}
第五步,如何調(diào)用
@Override public void onError(Throwable e) { //直接調(diào)用即可 ExceptionUtils.handleException(e); }06.完成版代碼展示
如下所示
public class ExceptionUtils {
/*
* 在使用Retrofit+RxJava時,我們訪問接口,獲取數(shù)據(jù)的流程一般是這樣的:訂閱->訪問接口->解析數(shù)據(jù)->展示。
* 如上所說,異常和錯誤本質(zhì)是一樣的,因此我們要盡量避免在View層對錯誤進行判斷,處理。
*
* 在獲取數(shù)據(jù)的流程中,訪問接口和解析數(shù)據(jù)時都有可能會出錯,我們可以通過攔截器在這兩層攔截錯誤。
* 1.在訪問接口時,我們不用設(shè)置攔截器,因為一旦出現(xiàn)錯誤,Retrofit會自動拋出異常。
* 2.在解析數(shù)據(jù)時,我們設(shè)置一個攔截器,判斷Result里面的code是否為成功,如果不成功,則要根據(jù)與服務(wù)器約定好的錯誤碼來拋出對應(yīng)的異常。
* 3.除此以外,為了我們要盡量避免在View層對錯誤進行判斷,處理,我們必須還要設(shè)置一個攔截器,攔截onError事件,然后使用ExceptionHandler,讓其根據(jù)錯誤類型來分別處理。
*/
/**
* 對應(yīng)HTTP的狀態(tài)碼
*/
private static final int BAD_REQUEST = 400;
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int METHOD_NOT_ALLOWED = 405;
private static final int REQUEST_TIMEOUT = 408;
private static final int CONFLICT = 409;
private static final int PRECONDITION_FAILED = 412;
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;
/**
* 服務(wù)器定義的狀態(tài)嗎
* 比如:登錄過期,提醒用戶重新登錄;
* 添加商品,但是服務(wù)端發(fā)現(xiàn)庫存不足,這個時候接口請求成功,服務(wù)端定義業(yè)務(wù)層失敗,服務(wù)端給出提示語,客戶端進行吐司
* 請求接口,參數(shù)異?;蛘哳愋湾e誤,請求code為200成功狀態(tài),不過給出提示,這個時候客戶端用log打印服務(wù)端給出的提示語,方便快遞查找問題
* 其他情況,接口請求成功,但是服務(wù)端定義業(yè)務(wù)層需要吐司服務(wù)端返回的對應(yīng)提示語
*/
/**
* 完全成功
*/
private static final int CODE_SUCCESS = 0;
/**
* Token 失效
*/
public static final int CODE_TOKEN_INVALID = 401;
/**
* 缺少參數(shù)
*/
public static final int CODE_NO_MISSING_PARAMETER = 400400;
/**
* 其他情況
*/
public static final int CODE_NO_OTHER = 403;
/**
* 統(tǒng)一提示
*/
public static final int CODE_SHOW_TOAST = 400000;
/**
* 這個可以處理服務(wù)器請求成功,但是業(yè)務(wù)邏輯失敗,比如token失效需要重新登陸
* @param code 自定義的code碼
*/
public static void serviceException(int code , String content){
if (code != CODE_SUCCESS){
ServerException serverException = new ServerException();
serverException.setCode(code);
serverException.setMessage(content);
handleException(serverException);
}
}
/**
* 這個是處理網(wǎng)絡(luò)異常,也可以處理業(yè)務(wù)中的異常
* @param e e異常
*/
public static void handleException(Throwable e){
HttpException ex;
//HTTP錯誤 網(wǎng)絡(luò)請求異常 比如常見404 500之類的等
if (e instanceof retrofit2.HttpException){
retrofit2.HttpException httpException = (retrofit2.HttpException) e;
ex = new HttpException(e, ErrorCode.HTTP_ERROR);
switch(httpException.code()){
case BAD_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case REQUEST_TIMEOUT:
case CONFLICT:
case PRECONDITION_FAILED:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
//均視為網(wǎng)絡(luò)錯誤
default:
ex.setDisplayMessage("網(wǎng)絡(luò)錯誤"+httpException.code());
break;
}
} else if (e instanceof ServerException){
//服務(wù)器返回的錯誤
ServerException resultException = (ServerException) e;
int code = resultException.getCode();
String message = resultException.getMessage();
ex = new HttpException(resultException, ErrorCode.SERVER_ERROR);
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("重新登陸");
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情況");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少參數(shù)");
break;
default:
ex.setDisplayMessage(message);
break;
}
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){
ex = new HttpException(e, ErrorCode.PARSE_ERROR);
//均視為解析錯誤
ex.setDisplayMessage("解析錯誤");
}else if(e instanceof ConnectException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//均視為網(wǎng)絡(luò)錯誤
ex.setDisplayMessage("連接失敗");
} else if(e instanceof java.net.UnknownHostException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//網(wǎng)絡(luò)未連接
ex.setDisplayMessage("網(wǎng)絡(luò)未連接");
} else if (e instanceof SocketTimeoutException) {
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//網(wǎng)絡(luò)未連接
ex.setDisplayMessage("服務(wù)器響應(yīng)超時");
} else {
ex = new HttpException(e, ErrorCode.UNKNOWN);
//未知錯誤
ex.setDisplayMessage("未知錯誤");
}
String displayMessage = ex.getDisplayMessage();
//這里直接吐司日志異常內(nèi)容,注意正式項目中一定要注意吐司合適的內(nèi)容
ToastUtils.showRoundRectToast(displayMessage);
}
}
其他介紹
1.技術(shù)博客匯總
2.開源項目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
github:github.com/yangchong21…
知乎:www.zhihu.com/people/yczb…
簡書:www.jianshu.com/u/b7b2c6ed9…
csdn:my.csdn.net/m0_37700275
喜馬拉雅聽書:www.ximalaya.com/zhubo/71989…
開源中國:my.oschina.net/zbj1618/blo…
泡在網(wǎng)上的日子:www.jcodecraeer.com/member/cont…
阿里云博客:yq.aliyun.com/users/artic… 239.headeruserinfo.3.dT4bcV
segmentfault頭條:segmentfault.com/u/xiangjian…
掘金:juejin.im/user/593943…
開源代碼案例:github.com/yangchong21…文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7056.html
目錄介紹 01.網(wǎng)絡(luò)請求異常分類 02.開發(fā)中注意問題 03.原始的處理方式 04.如何減少代碼耦合性 05.異常統(tǒng)一處理步驟 06.完成版代碼展示 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善……開源的文件是ma...
摘要:錯誤使用單利在開發(fā)中單例經(jīng)常需要持有對象,如果持有的對象生命周期與單例生命周期更短時,或?qū)е聼o法被釋放回收,則有可能造成內(nèi)存泄漏。如果集合是類型的話,那內(nèi)存泄漏情況就會更為嚴重。 目錄介紹 1.OOM和崩潰優(yōu)化 1.1 OOM優(yōu)化 1.2 ANR優(yōu)化 1.3 Crash優(yōu)化 2.內(nèi)存泄漏優(yōu)化 2.0 動畫資源未釋放 2.1 錯誤使用單利 2.2 錯誤使用靜態(tài)變量 2.3 ...
閱讀 736·2023-04-25 19:43
閱讀 3981·2021-11-30 14:52
閱讀 3807·2021-11-30 14:52
閱讀 3871·2021-11-29 11:00
閱讀 3802·2021-11-29 11:00
閱讀 3904·2021-11-29 11:00
閱讀 3580·2021-11-29 11:00
閱讀 6183·2021-11-29 11:00