Android RxJava+Retrofit 網路異常捕獲、狀態代碼統一處理
前言
近來使用RxJava+Retrofit進行開發,在項目中遇到這樣一個需求,連網請求獲得資料異常時,需要將對應的Message和StatusCode進行獲得並展示,比如:
1.伺服器串連Error: 對應的返回404,500等等;
2.沒有網路狀態(沒有4g,3g,是否處於wifi環境下等);
參考文章:
Rxjava +Retrofit 你需要掌握的幾個技巧,Retrofit緩衝,RxJava封裝,統一對有無網路處理,異常處理, 返回結果問題 @碼小白 一、簡單的擷取處理
最簡單的處理方式,直接對返回的throwable進行類型判斷處理:
//先判斷網路環境if(!NetUtils.is4G(context)){ return;}//再連網請求,對throwable進行判斷ServiceHelper.getInstance() .getModelResult(param1, param2) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Model>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { if(e instanceof HttpException){ //擷取對應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) { } });
顯然,代碼並不難以理解,但是非常麻煩,總不至於每次都要進行這樣的大塊代碼複製粘貼吧。
請注意,進行類型判斷的的HttpException為
import retrofit2.adapter.rxjava.HttpException;
所以需要我們依賴:
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
二、NetErrorUtil封裝
1.首先我們建立一個Throwable的管理類
public class ExceptionHandle { 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; public static ResponeThrowable handleException(Throwable e) { ResponeThrowable ex; Log.i("tag", "e.toString = " + e.toString()); if (e instanceof HttpException) { HttpException httpException = (HttpException) e; ex = new ResponeThrowable(e, ERROR.HTTP_ERROR); switch (httpException.code()) { case UNAUTHORIZED: case FORBIDDEN: case NOT_FOUND: case REQUEST_TIMEOUT: case GATEWAY_TIMEOUT: case INTERNAL_SERVER_ERROR: case BAD_GATEWAY: case SERVICE_UNAVAILABLE: default: //ex.code = httpException.code(); ex.message = "網路錯誤"; break; } return ex; } else if (e instanceof ServerException) { ServerException resultException = (ServerException) e; ex = new ResponeThrowable(resultException, resultException.code); ex.message = resultException.message; return ex; } else if (e instanceof JsonParseException || e instanceof JSONException /*|| e instanceof ParseException*/) { ex = new ResponeThrowable(e, ERROR.PARSE_ERROR); ex.message = "解析錯誤"; return ex; } else if (e instanceof ConnectException) { ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR); ex.message = "串連失敗"; return ex; } else if (e instanceof javax.net.ssl.SSLHandshakeException) { ex = new ResponeThrowable(e, ERROR.SSL_ERROR); ex.message = "認證驗證失敗"; return ex; } else { ex = new ResponeThrowable(e, ERROR.UNKNOWN); ex.message = "未知錯誤"; return ex; } } /** * 約定異常 */ class ERROR { /** * 未知錯誤 */ public static final int UNKNOWN = 1000; /** * 解析錯誤 */ public static final int PARSE_ERROR = 1001; /** * 網路錯誤 */ public static final int NETWORD_ERROR = 1002; /** * 協議出錯 */ public static final int HTTP_ERROR = 1003; /** * 認證出錯 */ public static final int SSL_ERROR = 1005; } public static class ResponeThrowable extends Exception { public int code; public String message; public ResponeThrowable(Throwable throwable, int code) { super(throwable); this.code = code; } } /** * ServerException發生後,將自動轉換為ResponeThrowable返回 */ class ServerException extends RuntimeException { int code; String message; }}
在這個類中,我們把throwable進行類型統一判斷:
1.若為Exception,我們再進行判斷,根據不同種類的Exception,轉化為ResponeThrowable返回,並賦予不同的code和message;
2.若為RuntimeException,我們再轉換為ResponeThrowable返回; 總而言之,我們把擷取到的異常,通過handleException()方法,轉化為統一的ResponeThrowable返回,這個對象包含code和message。 2.然後是建立一個新的Subscriber基類
/** * Subscriber基類,可以在這裡處理client網路連接狀況 * (比如沒有wifi,沒有4g,沒有連網等) * Created by fcn-mq on 2017/4/19. */public abstract class MySubscriber<T> extends Subscriber<T> { private Context context; public MySubscriber(Context context) { this.context = context; } @Override public void onStart() { super.onStart(); Log.i("tag","MySubscriber.onStart()"); //接下來可以檢查網路連接等操作 if (!NetworkUtil.isNetworkAvailable(context)) { Toast.makeText(context, "當前網路不可用,請檢查網路情況", Toast.LENGTH_SHORT).show(); // 一定好主動調用下面這一句,取消本次Subscriber訂閱 if (!isUnsubscribed()) { unsubscribe(); } return; } } @Override public void onError(Throwable e) { Log.e("tag","MySubscriber.throwable ="+e.toString()); Log.e("tag","MySubscriber.throwable ="+e.getMessage()); if(e instanceof Exception){ //訪問獲得對應的Exception onError(ExceptionHandle.handleException(e)); }else { //將Throwable 和 未知錯誤的status code返回 onError(new ExceptionHandle.ResponeThrowable(e,ExceptionHandle.ERROR.UNKNOWN)); } } public abstract void onError(ExceptionHandle.ResponeThrowable responeThrowable); @Override public void onCompleted() { Log.i("tag","MySubscriber.onComplete()"); }}
這個就好理解了,我們可以在基類中統一進行用戶端網路環境的判斷,或者當網路異常發生時,統一轉換為
ResponeThrowable,在onError(ExceptionHandle.ResponeThrowable responeThrowable)中回調,這個abstract的回調方法需要我們自己實現。 3.代碼中使用:
new Retrofit.Builder() .baseUrl(ConstantsApi.BASE_DOUBAN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(client) .build() .create(DoubanMovieService.class)//獲得對應的service對象 .getModelResult(param1, param2) //網路請求 .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new MySubscriber<Model>(context) { @Override public void onNext(Model model) { ... } @Override public void onError(ExceptionHandle.ResponeThrowable throwable) { LogUtil.m("tag","throwable =" + throwable.toString()); //接下來就可以根據狀態代碼進行處理... int statusCode = throwable.code; switch (statusCode){ case ExceptionHandle.ERROR.SSL_ERROR: break; case ExceptionHandle.ERROR.UNKNOWN: break; case ExceptionHandle.ERROR.PARSE_ERROR: break; case ExceptionHandle.ERROR.NETWORD_ERROR: break; case ExceptionHandle.ERROR.HTTP_ERROR: break; } } });
最後是NetErrorUtil的源碼地址:點我點我
【GitHub地址】https://github.com/ButQingMei/Samples.git