Retrofit統一異常處理

張欽發表於2018-02-27

一、雜談

前一陣子部落格備案因為名字問題被駁回了兩次也是夠了,現在在公司裡一直寫業務程式碼,這讓本來就不會的演算法的我演算法水平更加爛,最近在跟著優酷上的一個小姐姐學魔方,智商跟不太上了啊哈哈哈哈哈。OK,步入正題,名字叫Retrofit異常處理,可是內容應該主要是針對網路的統一異常處理,我之前的異常處理都是在BaseActivity或者是BaseFragment中去新增一個方法,然後在網路請求有問題時去呼叫這個方法,但是後來我把我用頻率較多的程式碼(包括Base)打包釋出了一個倉庫AndroidQuick,這樣的話我的異常處理就沒法在Base層裡面處理了,而且本身的處理方式就有很多不完善的地方。

二、BaseResponse(Response基類)

在BaseResponse中判斷請求有無錯誤(判斷與後臺約定的code等),如果不正確,帶著code呼叫NetworkError網路統一異常處理類

  1. 介面示例

    @FormUrlEncoded
    @POST(Constants.BASE_API + "sendCode")
    Flowable<BaseResponse<SendCodeBean>> getMobileCode(@FieldMap Map<String, String> values);
    複製程式碼
  2. BaseResponse

    public class BaseResponse<T> {
    
    	private int code;
    	private String msg;
    	private T res;
    
    	/**
    	* 這個方法時已經成功訪問後臺了,code是後臺約定的錯誤碼,判斷訪問是否成功
    	*
    	* @param context 在做異常處理的時候可能涉及到跳轉Activity
    	* @return 返回成功或失敗
    	*/
    	public boolean isOk(Context context) {
    		if (code == 200) {
    			return true;
    		} else {
    			NetworkError.error(context, new ServerException(code, msg));
    			return false;
    		}
    	}
    
    	// get/set方法...
    }
    複製程式碼

三、NetworkError(網路統一異常處理類)

  • 不太會寫文章,我在程式碼裡做了詳細註釋。

  • 程式碼

    public class NetworkError {
    
    	/**
    	* @param context 可以用於跳轉Activity等操作
    	*/
    	public static void error(Context context, Throwable throwable) {
    		RetrofitException.ResponeThrowable responeThrowable = RetrofitException.retrofitException(throwable);
    		// 此處可以通過判斷錯誤程式碼來實現根據不同的錯誤程式碼做出相應的反應
    		switch (responeThrowable.code) {
    			case RetrofitException.ERROR.UNKNOWN:
    			case RetrofitException.ERROR.PARSE_ERROR:
    			case RetrofitException.ERROR.NETWORD_ERROR:
    			case RetrofitException.ERROR.HTTP_ERROR:
    			case RetrofitException.ERROR.SSL_ERROR:
    				Toast.makeText(context, responeThrowable.message, Toast.LENGTH_SHORT).show();
    				break;
    			case -1:
    				// 跳轉到登陸頁面
    				context.startActivity(new Intent(context, LoginActivity.class));
    				// 結束除LoginActivity之外的所有Activity
    				AppManager.finishAllActivity(LoginActivity.class);
    				break;
    			default:
    				Toast.makeText(context, responeThrowable.message, Toast.LENGTH_SHORT).show();
    				break;
    		}
    	}
    }
    複製程式碼

四、ServerException(伺服器下發的異常)

public class ServerException extends RuntimeException {

    public int code;

    public ServerException(int code, String message) {
        super(message);
        this.code = code;
    }
}
複製程式碼

五、RetrofitException(網路異常類)

  • 這個類主要是通過instanceof操作符來判斷異常的型別

  • 程式碼

    public class RetrofitException {
    
    	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 retrofitException(Throwable e) {
    		ResponeThrowable ex;
    		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.message = "網路錯誤";
    					break;
    			}
    			return ex;
    		} else if (e instanceof ServerException) {
    			// 伺服器下發的錯誤
    			ServerException resultException = (ServerException) e;
    			ex = new ResponeThrowable(resultException, resultException.code);
    			ex.message = resultException.getMessage();
    			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
    				|| e instanceof SocketTimeoutException
    				|| e instanceof UnknownHostException) {
    			ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
    			ex.message = "連線失敗";
    			return ex;
    		} else if (e instanceof 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;
    		}
    	}
    }
    複製程式碼

六、使用方式

Map<String, String> map = new LinkedHashMap<>();
map.put("id", mId);
map.put("uid", SPUtils.getInstance().getString("uid"));
RetrofitClient
		// 單例呼叫
		.getInstance()
		// 獲取請求介面
		.gService
		// 呼叫介面方法
		.getMobileCode(map)
		// 通過compose切換執行緒
		.compose(RxUtil.rxSchedulerHelper())
		// 訂閱
		.subscribe(response -> {
			if (response.isOk(mContext)) {
				// 全部正確
			} else {
				// 伺服器下發的錯誤(BaseResponse的isOk()會呼叫異常處理方法)
			}
		}, throwable -> {
			LogUtils.e(throwable);
			// Retrofit異常
			NetworkError.error(context, throwable);
		});
複製程式碼

七、原始碼

https://github.com/sdwfqin/AndroidQuick

歡迎提供您寶貴的意見與建議,E-mail:zhangqin@sdwfqin.com

相關文章