RxRetrofit - 終極封裝 - 深入淺出 & 異常

wzgiceman發表於2016-12-30

背景

在前面Rxjava+ReTrofit+okHttp深入淺出-終極封裝專欄我們已經全面的封裝了一套可以投入實戰的框架,最近QQ群中有兄弟說異常處理這塊可以優化優化並給出了建議參考專案,果斷重新將之前的封裝完善走起來,將請求過程中的處理統一封裝起來,回撥給呼叫者,根據自定義回撥型別方便查詢錯誤型別和資訊。


前提

本章的內容基於掌握了前面封裝的原理以後,學期起來才能完全的理解

Rxjava+ReTrofit+okHttp深入淺出-終極封裝專欄


效果:

RxRetrofit - 終極封裝 - 深入淺出 & 異常
這裡寫圖片描述

通過統一的異常處理,可以實現各種異常的統一處理,然後通過統一回撥給使用者,方便統一展示和顯示提示給使用者

  • 第一條錯誤:故意修改了service裡面方法地址,導致錯誤

  • 第二條錯誤:過期token,伺服器返回的錯誤資訊


優化之路

1.定義回撥異常類

定義的回撥類,方便回撥介面統一處理,其中包含錯誤code和錯誤資訊displayMessage

public class ApiException extends Exception{
    /*錯誤碼*/
    private int code;
    /*顯示的資訊*/
    private String displayMessage;

    public ApiException(Throwable e) {
        super(e);
    }

    public ApiException(Throwable cause,@CodeException.CodeEp int code, String showMsg) {
        super(showMsg, cause);
        setCode(code);
        setDisplayMessage(showMsg);
    }

    @CodeException.CodeEp
    public int getCode() {
        return code;
    }

    public void setCode(@CodeException.CodeEp int code) {
        this.code = code;
    }

    public String getDisplayMessage() {
        return displayMessage;
    }

    public void setDisplayMessage(String displayMessage) {
        this.displayMessage = displayMessage;
    }
}複製程式碼

2.定義錯誤碼

自定義錯誤碼,相關的錯誤碼可以自行設定規則,框架現在給出了常用的錯誤碼定義,採用上一章講解的Android註解方式來定義錯誤碼的使用:

public class CodeException {

    /*網路錯誤*/
    public static final int NETWORD_ERROR = 0x1;
    /*http_錯誤*/
    public static final int HTTP_ERROR = 0x2;
    /*fastjson錯誤*/
    public static final int JSON_ERROR = 0x3;
    /*未知錯誤*/
    public static final int UNKNOWN_ERROR = 0x4;
    /*執行時異常-包含自定義異常*/
    public static final int RUNTIME_ERROR = 0x5;
    /*無法解析該域名*/
    public static final int UNKOWNHOST_ERROR = 0x6;


    @IntDef({NETWORD_ERROR, HTTP_ERROR, RUNTIME_ERROR, UNKNOWN_ERROR, JSON_ERROR, UNKOWNHOST_ERROR})
    @Retention(RetentionPolicy.SOURCE)

    public @interface CodeEp {
    }

}複製程式碼

因為是在Rxjava+ReTrofit+okHttp深入淺出-終極封裝六特殊篇(變種String替換Gson自由擴充套件)基礎上完善的異常處理,這裡解析使用的是 fastjson的異常定義json解析異常

3.完善自定義執行時異常

HttpTimeException類在之前的封裝中就已經存在,通過它在處理伺服器返回錯誤資訊和快取錯誤資訊,所以我們只是完善它的呼叫規則,讓它更加合理

public class HttpTimeException extends RuntimeException {
    /*未知錯誤*/
    public static final int UNKOWN_ERROR = 0x1002;
    /*本地無快取錯誤*/
    public static final int NO_CHACHE_ERROR = 0x1003;
    /*快取過時錯誤*/
    public static final int CHACHE_TIMEOUT_ERROR = 0x1004;


    public HttpTimeException(int resultCode) {
        this(getApiExceptionMessage(resultCode));
    }

    public HttpTimeException(String detailMessage) {
        super(detailMessage);
    }

    /**
     * 轉換錯誤資料
     *
     * @param code
     * @return
     */
    private static String getApiExceptionMessage(int code) {
        switch (code) {
            case UNKOWN_ERROR:
                return "錯誤:網路錯誤";
            case NO_CHACHE_ERROR:
                return "錯誤:無快取資料";
            case CHACHE_TIMEOUT_ERROR:
                return "錯誤:快取資料過期";
            default:
                return "錯誤:未知錯誤";
        }
    }
}複製程式碼

完善後:加入code碼和對應的錯誤資訊

4.建立異常工廠類

異常工廠類中,通過傳入對應的Throwable錯誤,然後根據Throwable的不同型別,生成不同的與之對應的ApiException異常,最後將ApiException異常返回給最後的rx回撥onerror方法,最後onerror方法統一對異常進行處理(如果你的需求又這樣的要求)回撥給使用者介面;

public class FactoryException {
    private static final String HttpException_MSG = "網路錯誤";
    private static final String ConnectException_MSG = "連線失敗";
    private static final String JSONException_MSG = "fastjeson解析失敗";
    private static final String UnknownHostException_MSG = "無法解析該域名";

    /**
     * 解析異常
     *
     * @param e
     * @return
     */
    public static ApiException analysisExcetpion(Throwable e) {
        ApiException apiException = new ApiException(e);
        if (e instanceof HttpException) {
             /*網路異常*/
            apiException.setCode(CodeException.HTTP_ERROR);
            apiException.setDisplayMessage(HttpException_MSG);
        } else if (e instanceof HttpTimeException) {
             /*自定義執行時異常*/
            HttpTimeException exception = (HttpTimeException) e;
            apiException.setCode(CodeException.RUNTIME_ERROR);
            apiException.setDisplayMessage(exception.getMessage());
        } else if (e instanceof ConnectException||e instanceof SocketTimeoutException) {
             /*連結異常*/
            apiException.setCode(CodeException.HTTP_ERROR);
            apiException.setDisplayMessage(ConnectException_MSG);
        } else if (e instanceof JSONPathException || e instanceof JSONException || e instanceof ParseException) {
             /*fastjson解析異常*/
            apiException.setCode(CodeException.JSON_ERROR);
            apiException.setDisplayMessage(JSONException_MSG);
        }else if (e instanceof UnknownHostException){
            /*無法解析該域名異常*/
            apiException.setCode(CodeException.UNKOWNHOST_ERROR);
            apiException.setDisplayMessage(UnknownHostException_MSG);
        } else {
            /*未知異常*/
            apiException.setCode(CodeException.UNKNOWN_ERROR);
            apiException.setDisplayMessage(e.getMessage());
        }
        return apiException;
    }
}複製程式碼

這個異常工廠類中的異常判斷在實際開發中,可以動態的自己新增,可以將分類更加細化完善!

5.rx錯誤異常的轉換

rx在連結呼叫過程中產生的異常預設是通過Subscriber的onError(Throwable e)方法回撥,這裡我們需要將Throwable 轉換成自定義ApiException回撥,所以需要呼叫rxjava中的onErrorResumeNext方法,在異常回撥前通過異常工廠類FactoryException處理返回統一的ApiException

虛擬碼

****
******
********
 Observable observable = basePar.getObservable(httpService)
                /*失敗後的retry配置*/
                .retryWhen(new RetryWhenNetworkException())
                /*異常處理*/
                .onErrorResumeNext(funcException)

**********

  /**
     * 異常處理
     */
    Func1 funcException = new Func1<Throwable, Observable>() {
        @Override
        public Observable call(Throwable throwable) {
            return Observable.error(FactoryException.analysisExcetpion(throwable));
        }
    };複製程式碼

6.回撥結果的統一處理

  • 1.因為改為統一的錯誤毀掉型別,需要修改之前的回到介面類
/**
 * 成功回撥處理
 * Created by WZG on 2016/7/16.
 */
public interface  HttpOnNextListener {
    /**
     * 成功後回撥方法
     * @param resulte
     * @param mothead
     */
   void onNext(String resulte,String mothead);

    /**
     * 失敗
     * 失敗或者錯誤方法
     * 自定義異常處理
     * @param e
     */
    void onError(ApiException e);
}複製程式碼
  • 2.onError(Throwable e)回撥處理
 /**
     * 錯誤統一處理
     *
     * @param e
     */
    private void errorDo(Throwable e) {
        Context context = mActivity.get();
        if (context == null) return;
        HttpOnNextListener httpOnNextListener = mSubscriberOnNextListener.get();
        if (httpOnNextListener == null) return;
        if (e instanceof ApiException) {
            httpOnNextListener.onError((ApiException) e);
        } else if (e instanceof HttpTimeException) {
            HttpTimeException exception=(HttpTimeException)e;
            httpOnNextListener.onError(new ApiException(exception,CodeException.RUNTIME_ERROR,exception.getMessage()));
        } else {
            httpOnNextListener.onError(new ApiException(e, CodeException.UNKNOWN_ERROR,e.getMessage()));
        }
        /*可以在這裡統一處理錯誤處理-可自由擴充套件*/
        Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
    }複製程式碼

這裡可以統一對異常進行統一處理,預設現在是toast提示,當然也有回撥的傳遞

  • 3.顯示介面

    @Override
    public void onNext(String resulte, String mothead) {
       *****
    }

    @Override
    public void onError(ApiException e) {
        tvMsg.setText("失敗:\ncode=" + e.getCode()+"\nmsg:"+e.getDisplayMessage());
    }複製程式碼

最後統一回撥在onError中傳遞迴一個ApiException物件


終極封裝專欄

RxJava+Retrofit+OkHttp深入淺出-終極封裝專欄)


專案地址

傳送門-GitHub-戳我


建議

如果你對這套封裝有任何的問題和建議歡迎加入QQ群告訴我!

相關文章