通常从服务端拿到的JSON数据格式大概如下:

  {"code":1,"message":"查询成功","detail":{"aa":"123","bb":"123","cc":"123"}}

因此通常我们会定义一个实体类来解析对应的json:

public class Response {@SerializedName("code")private int code;@SerializedName("message")private String message;@SerializedName("detail")private DetailBean detail;//省略getter和setter方法...public static class DetailBean {@SerializedName("aa")private String aa;@SerializedName("bb")private String bb;@SerializedName("cc")private String cc;//省略getter和setter方法...}
}

其中的code字段表示状态,比如以下值可能代表了不同的含义

  • code = 1, 表示成功, 不等于1代表错误
  • code = -101, 表示token过期
  • code = -102, 表示手机号码已经注册
  • 等等等
    如果我们按照正常的Retrofit+RxJava逻辑来处理,写出来的代码如下所示:
//ApiService.java
public interface ApiService {String ENDPOINT = Constants.END_POINT;@POST("app/api")Observable<Response1> request1(@Body Request1 request);@POST("app/api")Observable<Response2> request2(@Body Request2 request);/*** Create a new ApiService*/
    class Factory {private Factory() {  }public static ApiService createService( ) {OkHttpClient.Builder builder = new OkHttpClient().newBuilder();builder.readTimeout(10, TimeUnit.SECONDS);builder.connectTimeout(9, TimeUnit.SECONDS);if (BuildConfig.DEBUG) {HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);builder.addInterceptor(interceptor);}builder.addInterceptor(new HeaderInterceptor());OkHttpClient client = builder.build();Retrofit retrofit =new Retrofit.Builder().baseUrl(ApiService.ENDPOINT).client(client).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();return retrofit.create(ApiService.class);}}
}

使用的时候:

ApiService mApiService = ApiService.Factory.createService();
mApiService.request1(request).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Response1>() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onNext(Response1 response) {int code = response.getCode();switch (code) {case 1: //do somethingbreak;case -101://do somethingbreak;case -102: //do somethingbreak;default:break;}}});

如果对每一个请求都这么做,那不是写死个人吗, 万一哪天这些值变了, 比如从-102 变成了 -105 , 那你不是每个地方全部都得改, 想想就可怕!

解决办法

Retrofit retrofit =new Retrofit.Builder().baseUrl(ApiService.ENDPOINT).client(client).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();

addConverterFactory(GsonConverterFactory.create()) 这句代码是为了用Gson把服务端返回的json数据解析成实体的, 那就从这里入手,可以自己定义一个GsonConverter,扩展一下原来的功能

先分析一下默认的GsonConverter怎么写的, 由三个类组成:

  • GsonConverterFactory // GsonConverter 工厂类, 用来创建GsonConverter
  • GsonResponseBodyConverter // 处理ResponseBody
  • GsonRequestBodyConverter // 处理RequestBody

从名字就很容易看出每个类是干嘛的, GsonResponseBodyConverter这个类肯定是关键, 看一下这个类:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {private final Gson gson;private final TypeAdapter<T> adapter;GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {this.gson = gson;this.adapter = adapter;}@Override public T convert(ResponseBody value) throws IOException {JsonReader jsonReader = gson.newJsonReader(value.charStream());try {return adapter.read(jsonReader);} finally {value.close();}}
}

你没有看错,就是这么几行代码… 这个convert()方法就是要扩展的地方了,

只需要在原来的逻辑上面添加上处理code ! = 1 的情况, 如果code ! = 1,就抛出异常,

先直接上代码:

//CustomGsonConverterFactory.java
public class CustomGsonConverterFactory extends Converter.Factory {private final Gson gson;private CustomGsonConverterFactory(Gson gson) {if (gson == null) throw new NullPointerException("gson == null");this.gson = gson;}public static CustomGsonConverterFactory create() {return create(new Gson());}public static CustomGsonConverterFactory create(Gson gson) {return new CustomGsonConverterFactory(gson);}@Overridepublic Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new CustomGsonResponseBodyConverter<>(gson, adapter);}@Overridepublic Converter<?, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new CustomGsonRequestBodyConverter<>(gson, adapter);}
}
//CustomGsonRequestBodyConverter.java
final class CustomGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");private static final Charset UTF_8 = Charset.forName("UTF-8");private final Gson gson;private final TypeAdapter<T> adapter;CustomGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {this.gson = gson;this.adapter = adapter;}@Overridepublic RequestBody convert(T value) throws IOException {Buffer buffer = new Buffer();Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);JsonWriter jsonWriter = gson.newJsonWriter(writer);adapter.write(jsonWriter, value);jsonWriter.close();return RequestBody.create(MEDIA_TYPE, buffer.readByteString());}
}
//CustomGsonResponseBodyConverter.java
final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {private final Gson gson;private final TypeAdapter<T> adapter;CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {this.gson = gson;this.adapter = adapter;}@Overridepublic T convert(ResponseBody value) throws IOException {String response = value.string();HttpStatus httpStatus = gson.fromJson(response, HttpStatus.class);if (httpStatus.isCodeInvalid()) {value.close();throw new ApiException(httpStatus.getCode(), httpStatus.getMessage());}MediaType contentType = value.contentType();Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;InputStream inputStream = new ByteArrayInputStream(response.getBytes());Reader reader = new InputStreamReader(inputStream, charset);JsonReader jsonReader = gson.newJsonReader(reader);try {return adapter.read(jsonReader);} finally {value.close();}}
}

他两个类和默认的一样的, 只看第三个类CustomGsonResponseBodyConverter

这里自定义了两个类,一个是HttpStatus和ApiException,下面是这两个类:

//HttpStatus.java
public class HttpStatus {@SerializedName("code")private int mCode;@SerializedName("message")private String mMessage;public int getCode() {return mCode;}public String getMessage() {return mMessage;}/*** API是否请求失败** @return 失败返回true, 成功返回false*/public boolean isCodeInvalid() {return mCode != Constants.WEB_RESP_CODE_SUCCESS;}
}
//ApiException.java
public class ApiException extends RuntimeException {private int mErrorCode;public ApiException(int errorCode, String errorMessage) {super(errorMessage);mErrorCode = errorCode;}/*** 判断是否是token失效** @return 失效返回true, 否则返回false;*/public boolean isTokenExpried() {return mErrorCode == Constants.TOKEN_EXPRIED;}
}

很通俗易懂, 解释一下其中关键的几行代码

 String response = value.string(); //把responsebody转为string
// 这里只是为了检测code是否==1,所以只解析HttpStatus中的字段,因为只要code和message就可以了HttpStatus httpStatus = gson.fromJson(response, HttpStatus.class); if (httpStatus.isCodeInvalid()) {value.close();//抛出一个RuntimeException, 这里抛出的异常会到Subscriber的onError()方法中统一处理throw new ApiException(httpStatus.getCode(), httpStatus.getMessage());}

这里有个关于ResponseBody的坑, 如果有人遇到过这个异常的肯定就知道

java.lang.IllegalStateException: closedat com.squareup.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:455)at okio.Buffer.writeAll(Buffer.java:594)at okio.RealBufferedSource.readByteArray(RealBufferedSource.java:87)at com.squareup.okhttp.ResponseBody.bytes(ResponseBody.java:56)at com.squareup.okhttp.ResponseBody.string(ResponseBody.java:82)

因为你只能对ResponseBody读取一次 , 如果你调用了response.body().string()两次或者response.body().charStream()两次就会出现这个异常, 先调用string()再调用charStream()也不可以.

所以通常的做法是读取一次之后就保存起来,下次就不从ResponseBody里读取.

使用方法

先建立一个BaseSubscriber

//BaseSubscriber.java
public class BaseSubscriber<T> extends Subscriber<T> {protected Context mContext;public BaseSubscriber(Context context) {this.mContext = context;}@Overridepublic void onCompleted() {}@Overridepublic void onError(final Throwable e) {Log.w("Subscriber onError", e);if (e instanceof HttpException) {// We had non-2XX http errorToast.makeText(mContext, mContext.getString(R.string.server_internal_error), Toast.LENGTH_SHORT).show();} else if (e instanceof IOException) {// A network or conversion error happenedToast.makeText(mContext, mContext.getString(R.string.cannot_connected_server), Toast.LENGTH_SHORT).show();} else if (e instanceof ApiException) {ApiException exception = (ApiException) e;if (exception.isTokenExpried()) {//处理token失效对应的逻辑} else {Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show();}} }@Overridepublic void onNext(T t) {}}

请求接口

ApiService mApiService = ApiService.Factory.createService();
mApiService.request1(request).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseSubscriber<Response1>() {@Overridepublic void onCompleted() {super.onCompleted(); }@Overridepublic void onError(Throwable e) {super.onError(e); //这里就全部交给基类来处理了}@Overridepublic void onNext(Response1 response) {super.onNext(response);}});

来自简书:http://www.jianshu.com/p/5b8b1062866b

Retrofit自定义GsonConverter处理请求错误异常处理相关推荐

  1. Laravel 5.5 的错误异常处理机制以及应用实例

    一.前言 我们在开发项目中,难免会因为逻辑上的失误而报错,这些报错的展现形式也就是框架封装好的异常处理机制.在项目上线之前,我们还可以根据框架提供的报错信息来锁定错误代码的位置.但是项目上线之后我们是 ...

  2. Kotlin使用Coroutine+ViewModel+retrofit构建一个网络请求框架

    Kotlin使用Coroutine+ViewModel+retrofit构建一个网络请求框架 公司里的老代码用的网络请求框架技术都比较老,为了快速搭建一个网络请求框架,提高工作效率,记录一下用jetp ...

  3. 浅谈Go 错误异常处理方式和并发陷进

    任何一门开发语言中,错误和异常处理都是不可回避的问题,Go 中主要通过 error 和 panic 分别表示错误和异常,并提供了较为简洁的错误异常处理机制.作为一门并发性能优越的语言,Go 虽然降低了 ...

  4. 400错误请求怎么解决_什么是400错误请求错误(以及如何解决)?

    400错误请求怎么解决 A 400 Bad Request Error occurs when a request sent to the website server is incorrect or ...

  5. 鸿蒙harmonyOS开发APP时,使用Retrofit+Java在get请求时遇到 ?pageNum=1pageSize=10的url 的get请求的写法

    鸿蒙harmonyOS开发APP时,使用Retrofit+Java在get请求时替换URL的时候会出现一个错误 java.lang.IllegalArgumentException: URL quer ...

  6. php的错误异常处理

    php的错误异常机制 当我们没有自定义错误异常处理函数,php会按照默认的方式处理和显示错误异常. 自定义默认的错误异常处理函数 //注册捕获错误的处理函数 set_error_handler([__ ...

  7. Go语言的错误异常处理机制及其应用

    一.背景 在日常编写golang程序或阅读别人的golang代码时,我们总会看到如下的一堆代码块: xx, err = func(xx) if err != nil {//do sth. to tac ...

  8. springmvc请求参数异常处理

    springmvc请求参数异常处理 参考文章: (1)springmvc请求参数异常处理 (2)https://www.cnblogs.com/nosqlcoco/p/5844160.html 备忘一 ...

  9. legend3---3、lavarel页面post请求错误之后跳转

    legend3---3.lavarel页面post请求错误之后跳转 一.总结 一句话总结: 控制器:return back()->withInput()->with('error','验证 ...

最新文章

  1. Android中实现震动的方法
  2. 基础练习 杨辉三角形
  3. 实验二:进程的创建与可执行程序的加载
  4. Android NDK开发一:配置环境
  5. sql中where和on的区别
  6. zabbix远程mysql_zabbix action 执行远程命令
  7. 在线场景感知:图像稀疏表示-ScSPM和LLC总结(lasso族、岭回归)
  8. 50个直击灵魂的问题_直击灵魂的问题:“妈妈,我还能要个哥哥不!”
  9. Nginx中 --- 内存池
  10. 两个平面的位置关系和判定方程组解_2018年高考数学总复习第九章平面解析几何第2讲两直线的位置关系学案!...
  11. 中投 汇金 中金 中登
  12. 前端之图形学-1 数据可视化
  13. 阿里 3月25日 二维矩阵列选元素最小和
  14. Pwnable之passcode
  15. 明星代言的商品一般都不便宜,为何还那么受青睐?
  16. 推荐几个阿里,百度大佬的订阅号给大家
  17. Sublime Text 3.0版本的傻瓜式汉化步骤
  18. nz-select选项无法默认显示
  19. Python代码实现PID控制
  20. 【开发记录】基于C++,使用QT+VS编写软件

热门文章

  1. C#实现汉字转拼音(包括生僻字)
  2. 我亲爱的朋友们_亲爱的lazyweb-我该如何处理所有电子邮件?
  3. Python实现去除图片中的数字水印
  4. 尚硅谷Java零基础极速入门七天版笔记
  5. 【算法】NP完全问题以及世界数学七大难题
  6. 超全面,带你了解UI设计全流程!
  7. 第一次数据分析师面试
  8. 怎样控制animate.css的动画时间
  9. 神经网络原理与实例精解,神经网络计算工作原理
  10. Matplotlib画各种论文图