背景
在前面我们已经全面的封装了一套可以投入实战的框架,最近QQ
群中有兄弟说异常处理这块可以优化优化并给出了建议,果断重新将之前的封装完善走起来,将请求过程中的处理统一封装起来,回调给调用者,根据自定义回调类型方便查询错误类型和信息。
前提
本章的内容基于掌握了前面封装的原理以后,学期起来才能完全的理解
效果:
通过统一的异常处理,可以实现各种异常的统一处理,然后通过统一回调给使用者,方便统一展示和显示提示给用户
第一条错误:故意修改了
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.定义错误码
自定义错误码,相关的错误码可以自行设定规则,框架现在给出了常用的错误码定义,采用上一章讲解的来定义错误码的使用:
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 { }}复制代码
因为是在基础上完善的异常处理,这里解析使用的是 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() { @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
对象