android retrofit 實戰自定義converter,解決相同介面返回不同資料的問題
square的retrofit是目前比較火的網路框架,我目前也在用
今天專案上遇到一個問題,就是請求同一個介面,可能返回不同的json格式
例如,訪問一個登入介面,成功的時候,返回的是
{
"code": 0,
"message": "登入成功",
"data": {
"username": "xxx",
"userId": "xxx"
}
}
我們首先定義一個basebean
public class Base {
public String message;
public int code;
}
然後定義一個loginbean去接收資料
public class LoginBean extends Base{ public LoginContent data; public class LoginContent { public String username; public String userId; } }
訪問失敗的時候,伺服器自動轉發了地址,返回伺服器維護的提示
{
"code": 1,
"message": "伺服器維護中",
"data": {
"title": "xxx",
"content": "xxx"
}
}
而我們通常用retrofit寫介面,都是通過下面這種方式
這裡把泛型寫死成了loginbean,@FormUrlEncoded @POST("user/login") Observable<LoginBean> login(@Field("userInfo.mobile") String mobile, @Field("userInfo.passWord") String pwd);
而我們通常在初始化時候,都會新增如下程式碼
new Retrofit.Builder() .baseUrl(Constants.SERVER_URL) .addConverterFactory(GsonConverterFactory.create()) //添加回調庫,採用RxJava .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(genericClient()) .build();
這裡的
GsonConverterFactory.create()
返回的是一個converterFactory,就是用於我們的json解析的
如果我們用的converter是預設的GsonConverter的話,retrofit就會按照
loginbean的格式解析,當訪問異常的時候,json返回的格式變了,json自然就解析失敗了。
於是我想過很多方法,例如把泛型換成base,到回撥的時候再繼續解析
但是想想,以後所有的介面都要這麼寫,豈不是要吐血?
那麼,我們如何通過不修改泛型,又可以實現資料的正常解析呢?
我採用的解決辦法是自定義converter
首先在base中擴充套件一個欄位
public class Base {
public String message;
public int code;
public Notify notify;
}
不多說,Notify的程式碼就不貼了,看converter
public class MyGsonResponseBodyConverter<T extends Base> implements Converter<ResponseBody, T> {
private final Gson gson;
private final Type type;
public MyGsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
@Override
public T convert(ResponseBody value) throws IOException {
String json = value.string();
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
// 解析code
JsonPrimitive jsonPrimitive = jsonObject.getAsJsonPrimitive("code");
int code = 0;
if (jsonPrimitive != null) {
code = jsonPrimitive.getAsInt();
}
// 解析message
JsonElement jsonElement = jsonObject.get("message");
String message = null;
if (jsonElement != null) {
massage = jsonElement.getAsString();
}
T t = null;
try {
// 通過反射獲取泛型的例項物件
Class<T> clazz = (Class<T>) type;
t = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
if (code == 1 ) {
// 按停服公告的格式解析,封裝到notify欄位中
t.notify = gson.fromJson(jsonObject.getAsJsonObject("data"), Notify.class);
} else {
// 按標準格式解析
return gson.fromJson(json, type);
}
return t;
}
}
上面程式碼可以看到,我們先去解析code,然後判斷不同的code去做不同的解析,
當返回的資料是停服公告時,就將解析到的data,封裝到notify裡面去,就可以避免json解析的異常
也可以正常返回資料
接下來自定義一個converterFactory
public 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);
}
}
其實就是把原始碼複製下來改了改。最後,把converter改成自己的converter
new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(MyGsonConverterFactory.create())
//添加回調庫,採用RxJava
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(genericClient())
.build();
不過這麼寫也有侷限性,就是當使用這個converter時,所有的bean都要繼承base類,
而且每當伺服器改變一種結構,我們都必須修改converter
這樣會相當麻煩,暫時沒有想到更好的解決辦法,如果有大神有更好的辦法歡迎指教
本人qq184497436