RxRetrofit - 终极封装 - 深入浅出 & 异常

3,434 阅读5分钟

背景

在前面Rxjava+ReTrofit+okHttp深入浅出-终极封装专栏我们已经全面的封装了一套可以投入实战的框架,最近QQ群中有兄弟说异常处理这块可以优化优化并给出了建议参考项目,果断重新将之前的封装完善走起来,将请求过程中的处理统一封装起来,回调给调用者,根据自定义回调类型方便查询错误类型和信息。


前提

本章的内容基于掌握了前面封装的原理以后,学期起来才能完全的理解

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

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群告诉我!