正常情况下我们开发app服务器返回的json一般是这样标准的形式。
{
"status": 1,
"msg": "message",
"data":{}
}
所以我们可以在客户端定义这样的格式来进行统一处理。
public class Result<T> {
public int status;
public String msg;
public T data;
}
最近遇到了这样的问题,服务器返回的data有时候是对象,有时候又是数组,还有可能是字符串,这样使用第三方解析就比较麻烦了。但是需要处理返回code不为0的情况,比如给用户一些提示什么的。所以写下来作为记录,防止以后再遇到。遇到这种坑,要么和后台协商返回统一格式的json字符串,如果后台不改,我们只能自己解决了。
一、使用自定义ConvertAdapter
Retrofit可以使用自定义的Converter,它的作用是可以将接受到的json转换成实体返回给我们。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiService.API_BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create()) // 就是这里
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.build();
一般情况下我们都使用GsonConverterFactory,当请求到json后,Retrofit就会调用GsonConverter将json转成我们需要的实体。
GsonConverterFactory一共只有三个类,并且代码量很少,如下:
public final class MyGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static MyGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static MyGsonConverterFactory create(Gson gson) {
return new MyGsonConverterFactory(gson);
}
private final Gson gson;
private MyGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new MyGsonResponseBodyConverter<>(gson, type);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new MyGsonRequestBodyConverter<>(gson, adapter);
}
}
final class MyGsonRequestBodyConverter<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;
MyGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public 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());
}
}
主要操作在这个类进行
public class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final Type type;
MyGsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
apiHandle = new ApiHandle();
}
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
try {
return gson.fromJson(response, type);
} finally {
value.close();
}
}
当我们发出一个POST JSON请求(直接用Retrofit post一个实体给服务器,请求的时候会自动将我们的实体转成Json)的时候,Retrofit就会调用GsonRequestBodyConverter的Convert方法将我们的实体转换成json。而接受服务器返回的数据时,Retrofit会调用GsonResponseBodyConverter将Json数据转换成我们需要的实体。
既然如此,我们可以在Converter解析json的时候就做服务器参数的统一处理
我是将GsonConverterFactory的三个类拷贝出来修改了一下:GsonConverterFactory和RequestBodyConverter几乎没有任何修改,我们需要更改类是GsonResponseBodyConverter,因为它才是将服务器的数据转换成实体了,我们在转换的过程中做统一处理。
这里需要说明的是因为后台返回的data时刻会改变,所以我们需要解决这个问题,我是这么处理的,首先自己定义一个类去存放code和message
public class BaseBean {
private int code;
private String msg;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
这样就能避免我们在解析data的时候出现解析错误,这里我的处理是在MyGsonResponseBodyConverter里面的convert方法拦截response
public T convert(ResponseBody value) throws IOException {
String response = value.string();
try {
BaseBean baseBean = gson.fromJson(response, BaseBean.class);//拦截code非0情况
if (baseBean.getCode() != 0) {
new Thread() {
@Override
public void run() {
Message msg = new Message();
msg.what = 1;
msg.obj = baseBean.getMsg();
handler.sendMessage(msg);
}
}.start();
}
return gson.fromJson(response, type);
} finally {
value.close();
}
}
这里我们判断code非0就是请求没有达到我们定义的预期效果,我这里是简单处理给了个吐司,也可以定义接口回调处理,但是因为是子线程所以要用handler更新ui。
一般情况下如果data格式固定可以在这里自定义异常处理,因为我是data不固定,只能先取cade和message简单处理。最好还是和后台小哥协商好最好了。最好不要遇到这种坑~~~