近来使用RxJava+Retrofit进行开发,在项目中遇到这样一个需求,联网请求获得数据异常时,需要将对应的Message和StatusCode进行获得并展示,比如:
1.服务器连接Error: 对应的返回404,500等等;
2.没有网络状态(没有4g,3g,是否处于wifi环境下等);
参考文章:
Rxjava +Retrofit 你需要掌握的几个技巧,Retrofit缓存,RxJava封装,统一对有无网络处理,异常处理, 返回结果问题 @码小白
最简单的处理方式,直接对返回的throwable进行类型判断处理:
//先判断网络环境
if(!NetUtils.is4G(context)){
return;
}
//再联网请求,对throwable进行判断
ServiceHelper.getInstance()
.getModelResult(param1, param2)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Model>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if(e instanceof HttpException){
//获取对应statusCode和Message
HttpException exception = (HttpException)e;
String message = exception.response().message();
int code = exception.response().code();
}else if(e instanceof SSLHandshakeException){
//接下来就是各种异常类型判断...
}else if(e instanceof ...){
}...
...
}
@Override
public void onNext(Model model) {
}
});
显然,代码并不难以理解,但是非常麻烦,总不至于每次都要进行这样的大块代码复制粘贴吧。
请注意,进行类型判断的的HttpException为
import retrofit2.adapter.rxjava.HttpException;
所以需要我们依赖:
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
public class ExceptionHandle {
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 handleException(Throwable e) {
ResponeThrowable ex;
Log.i("tag", "e.toString = " + e.toString());
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.code = httpException.code();
ex.message = "网络错误";
break;
}
return ex;
} else if (e instanceof ServerException) {
ServerException resultException = (ServerException) e;
ex = new ResponeThrowable(resultException, resultException.code);
ex.message = resultException.message;
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) {
ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
ex.message = "连接失败";
return ex;
} else if (e instanceof javax.net.ssl.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;
}
}
/**
* ServerException发生后,将自动转换为ResponeThrowable返回
*/
class ServerException extends RuntimeException {
int code;
String message;
}
}
在这个类中,我们把throwable进行类型统一判断:
1.若为Exception,我们再进行判断,根据不同种类的Exception,转化为ResponeThrowable返回,并赋予不同的code和message;
2.若为RuntimeException,我们再转换为ResponeThrowable返回;
/**
* Subscriber基类,可以在这里处理client网络连接状况
* (比如没有wifi,没有4g,没有联网等)
* Created by fcn-mq on 2017/4/19.
*/
public abstract class MySubscriber<T> extends Subscriber<T> {
private Context context;
public MySubscriber(Context context) {
this.context = context;
}
@Override
public void onStart() {
super.onStart();
Log.i("tag","MySubscriber.onStart()");
//接下来可以检查网络连接等操作
if (!NetworkUtil.isNetworkAvailable(context)) {
Toast.makeText(context, "当前网络不可用,请检查网络情况", Toast.LENGTH_SHORT).show();
// 一定好主动调用下面这一句,取消本次Subscriber订阅
if (!isUnsubscribed()) {
unsubscribe();
}
return;
}
}
@Override
public void onError(Throwable e) {
Log.e("tag","MySubscriber.throwable ="+e.toString());
Log.e("tag","MySubscriber.throwable ="+e.getMessage());
if(e instanceof Exception){
//访问获得对应的Exception
onError(ExceptionHandle.handleException(e));
}else {
//将Throwable 和 未知错误的status code返回
onError(new ExceptionHandle.ResponeThrowable(e,ExceptionHandle.ERROR.UNKNOWN));
}
}
public abstract void onError(ExceptionHandle.ResponeThrowable responeThrowable);
@Override
public void onCompleted() {
Log.i("tag","MySubscriber.onComplete()");
}
}
这个就好理解了,我们可以在基类中统一进行客户端网络环境的判断,或者当网络异常发生时,统一转换为
ResponeThrowable,在onError(ExceptionHandle.ResponeThrowable responeThrowable)中回调,这个abstract的回调方法需要我们自己实现。
new Retrofit.Builder()
.baseUrl(ConstantsApi.BASE_DOUBAN)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(client)
.build()
.create(DoubanMovieService.class)//获得对应的service对象
.getModelResult(param1, param2) //网络请求
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MySubscriber<Model>(context) {
@Override
public void onNext(Model model) {
...
}
@Override
public void onError(ExceptionHandle.ResponeThrowable throwable) {
LogUtil.m("tag","throwable =" + throwable.toString());
//接下来就可以根据状态码进行处理...
int statusCode = throwable.code;
switch (statusCode){
case ExceptionHandle.ERROR.SSL_ERROR:
break;
case ExceptionHandle.ERROR.UNKNOWN:
break;
case ExceptionHandle.ERROR.PARSE_ERROR:
break;
case ExceptionHandle.ERROR.NETWORD_ERROR:
break;
case ExceptionHandle.ERROR.HTTP_ERROR:
break;
}
}
});
最后是NetErrorUtil的源码地址:点我点我
【GitHub地址】https://github.com/ButQingMei/Samples.git
时隔一年,笔者发现本文的实现方式,在很多情况下有 很大的限制性(直白点吧,就是上文的方式太low了),因此修改了实现的方式,并归纳出一个在我看来 更灵活,更轻量 的工具组件,更适合 RxJava2的重度使用者,详情请参考:
不要打破链式调用!一个极低成本的RxJava2全局Error处理方案