問題
Retrofit 是目前主流的網(wǎng)絡(luò)請(qǐng)求框架趋距,使用Retrofit時(shí)采用數(shù)據(jù)轉(zhuǎn)化器直接轉(zhuǎn)化服務(wù)器數(shù)據(jù)時(shí)可能會(huì)因?yàn)榉?wù)器接口返回的同一字段數(shù)據(jù)類型不同而解析錯(cuò)誤,這就需要我們開發(fā)者處理這種情況,返回?cái)?shù)據(jù)比如這種:
{
"code": 500,
"msg": "登錄失敗",
"data": ""
}
{
"code": 500,
"msg": "登錄失敗",
"data": 0
}
{
"code": 500,
"msg": "登錄失敗",
"data": []
}
解決方案三種:
解決方案一:
接口人員制定接口規(guī)則龙考,同一接口同一字段在不同情況下類型需要保持一致,這樣就能很好的解決問題杀糯,也有利于后臺(tái)邏輯清晰條理喻杈。
很多事情不同后臺(tái)配合的人員的個(gè)人情況不同彤枢,求人不如求己,自己掌控任何時(shí)候都能游刃有余的處理好問題筒饰,于是就有了方案二和方案三缴啡。
解決方案二:
方案二是自定義Gson解析器,涉及三個(gè)類:GsonConverterFactory瓷们、GsonRequestBodyConverter(處理請(qǐng)求時(shí)的參數(shù)body)业栅、GsonResponseBodyConverter(處理響應(yīng)的數(shù)據(jù)body)
方案二也是很多博客中提到的,具體的做法我會(huì)在結(jié)尾附上一篇網(wǎng)友的博客谬晕,博客中也提到了如果用這種碘裕,一旦我們脫離了Retrofit框架,那么可能就無法處理這個(gè)數(shù)據(jù)類型不一致的問題了攒钳,那么我們就可以采用方案三的方式帮孔,攔截器來提前將數(shù)據(jù)做一些修改。
解決方案三(以我項(xiàng)目中的使用為例):
import androidx.annotation.NonNull;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.GzipSource;
public abstract class ResponseBodyInterceptor implements Interceptor {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
if (responseBody != null) {
long contentLength = responseBody.contentLength();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer();
if ("gzip".equals(response.headers().get("Content-Encoding"))) {
GzipSource gzippedResponseBody = new GzipSource(buffer.clone());
buffer = new Buffer();
buffer.writeAll(gzippedResponseBody);
}
MediaType contentType = responseBody.contentType();
Charset charset;
if (contentType == null || contentType.charset(StandardCharsets.UTF_8) == null) {
charset = StandardCharsets.UTF_8;
} else {
charset = contentType.charset(StandardCharsets.UTF_8);
}
if (charset != null && contentLength != 0L) {
return intercept(response,url, buffer.clone().readString(charset));
}
}
return response;
}
abstract Response intercept(@NonNull Response response,String url, String body);
}
import androidx.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class HandleErrorInterceptor extends ResponseBodyInterceptor{
@Override
Response intercept(@NonNull Response response, String url, String body) {
try {
JSONObject jsonObject = new JSONObject(body);
if(!url.startsWith("https://apis.map.qq.com")){
if (!"200".equals(jsonObject.optString("code"))) {
if (response.body() != null) {
JSONObject obj = new JSONObject(body);
if(obj.has("data")){
obj.put("data",null);
}
ResponseBody boy = ResponseBody.create(response.body().contentType(), obj.toString());
return response.newBuilder().body(boy).build();
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
}
我理解的這種方式關(guān)鍵點(diǎn)在于使用一次流就會(huì)被關(guān)閉釋放不撑,而我們是需要讀取返回結(jié)果是否符合我們定義的格式或者類型文兢,如果發(fā)現(xiàn)不同需要處理成我們解析需要的格式,然后再交給格式轉(zhuǎn)化器去轉(zhuǎn)化燎孟,上述代碼就是做了這些事情禽作。
寫在最后
本文基本引用了Android 優(yōu)雅地處理后臺(tái)返回的騷數(shù)據(jù)這篇文章,這篇文章還大量羅列了遇到的各種服務(wù)器返回格式的問題爆侣,可以看一看了解一下萍程。