假设,我们请求服务器后,正常的返回的数据结构是这样的:
{
"result": [
{
"user": "test",
"age": 30
}
],
"code": "0000",
"message": "操作成功"
}
但是如果,某些现实情况,返回的数据结构变成以下这样:
{
"result": [
{
"user": "test",
"age": "30"
}
],
"code": "0000",
"message": "操作成功"
}
在上面,result 数组中的对象里,age字段的数据类型由开始的整型30,变成了字符串类型 “30”,如无意外的话,这个对象会解析失败,然后就会导致界面显示的数据不完整等其他情况,因为解析过程被迫中断了。
那么,我们应该怎么做呢?
首先,在构建 Retrofit 实例的时候,一般是类似以下的代码:
mRetrofit = new Retrofit.Builder()
.client(mOkHttpClient)//设置自定义okHttp
.baseUrl(Constants.BASE_URL) //基础地址
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //配置RxJava适配
.addConverterFactory(GsonConverterFactory.create())//添加原装GSON解析
.build();
那么,这部分代码中,里面的 addConverterFactory 这个方法是用来添加 json 解释器的,我们要改动的地方就在这里。将上面的代码改成以下代码:
mRetrofit = new Retrofit.Builder()
.client(mOkHttpClient)//设置自定义okHttp
.baseUrl(Constants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //配置RxJava适配
.addConverterFactory(ResponseConverterFactory.create())//添加自定义GSON解析
.build();
你会发现,json 解释器我将 retrofit 提供的原生的 GsonConverterFactory 改成了 自定义的ResponseConverterFactory 。那么, 这个 ResponseConverterFactory 是哪里来的呢?来自如下的代码:
public class ResponseConverterFactory extends Converter.Factory {
private final Gson gson;
private ResponseConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
public static ResponseConverterFactory create() {
return create(new GsonBuilder().registerTypeAdapterFactory(new MyTypeAdapterFactory()).create());
}
public static ResponseConverterFactory create(Gson gson) {
return new ResponseConverterFactory(gson);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new GsonResponseBodyConverter<>(gson,type);
}
}
解释一下,上述代码中的自定义类分别有:
MyTypeAdapterFactory:数据类型相关的,例如 int ,float,string等等,你要处理什么数据类型,就将对应的丢进去
GsonResponseBodyConverter:定义了处理json数据的对象类(例如Gson)以及实例化目标的数据类(例如student对象类),
我们先看下 MyTypeAdapterFactory的代码:
public class MyTypeAdapterFactory<T> implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (rawType == Float.class || rawType == float.class) {
return (TypeAdapter<T>) new FloatNullAdapter();
}else if (rawType == Integer.class || rawType == int.class){
return (TypeAdapter<T>) new IntNullAdapter();
}
return null;
}
}
代码意思大概就是,当数据匹配到的类型的是 float (或者Float)类型的话,就返回一个 FloatNullAdapter 浮点型适配器这么一个东西,所以这个 FloatNullAdapter 是个啥东西
直接看代码:
public class FloatNullAdapter extends TypeAdapter<Float> {
@Override
public Float read(JsonReader reader) throws IOException {
// TODO Auto-generated method stub
if (reader.peek() == JsonToken.STRING) {
reader.skipValue(); //跳过当前
return -1f;
}
BigDecimal bigDecimal = new BigDecimal(reader.nextString());
return bigDecimal.floatValue();
}
@Override
public void write(JsonWriter writer, Float value) throws IOException {
// TODO Auto-generated method stub
writer.value(value);
}
}
作用大概就是,当发现当前数据预先设置的类型是float类型,而接收到的数据是字符串类型时候,跳过当前的数据,并且返回一个固定值 -1 ,当然这是我的写法,具体场景请各位道友自行根据需求编写。
然后,IntNullAdapter :
public class IntNullAdapter extends TypeAdapter<Number> {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return reader.nextInt();
} catch (NumberFormatException e) {
GyLog.e("NumberFormatException ->"+e.getMessage());
//这里解析int出错,那么捕获异常并且返回默认值,因为nextInt出错中断了方法,没有完成位移,所以调用nextString()方法完成位移。
reader.nextString();
return 0;
}
}
@Override
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
}
}
其实大概思路也差不多
所以,我们直接看 GsonResponseBodyConverter:
public class GsonResponseBodyConverter<T> implements Converter<ResponseBody,T> {
private final Gson gson;
private final Type type;
GsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
/**
* 针对数据返回成功、错误不同类型字段处理
*/
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
return gson.fromJson(response, type);
}
}
关键点在于,gson 这货是来自构造函数参数里的,所以,可以返回到 ResponseConverterFactory 这个类里,因为 GsonResponseBodyConverter 是在ResponseConverterFactory 里被new了。然后,再跟下去,你会发现 gson 的实例是由
GsonBuilder().registerTypeAdapterFactory(new MyTypeAdapterFactory()).create()
弄出来的,也就是我们定义了gson在处理 float 以及 int 类型数据的时候,要走 MyTypeAdapterFactory 里面的 FloatNullAdapter 以及 IntNullAdapter 两个类事先定义的逻辑,而不是之前默认的new Gson 出来的实例。
如此一来,也就达到了当初的需求。