RxJava+Retrofit+OkHttp深入淺出-終極封裝七(異常處理)

yangxi_001發表於2017-06-12

背景

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


前提

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

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


效果:

這裡寫圖片描述

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

  • 第一條錯誤:故意修改了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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

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 {
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

因為是在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 "錯誤:未知錯誤";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

完善後:加入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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

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

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));
        }
    };         
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 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();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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

  • 3.顯示介面

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

    @Override
    public void onError(ApiException e) {
        tvMsg.setText("失敗:\ncode=" + e.getCode()+"\nmsg:"+e.getDisplayMessage());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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


終極封裝專欄

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


專案地址

傳送門-GitHub-戳我


建議

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

相關文章