首先說問題:在做項目的過程中,需要在在調用接口的時候token在后臺失效的一個判斷,如果token失效了那么APP就重新登錄。我個人認為,后臺應該返回的是response.code()的這個狀態碼為自定義狀態碼,然后根據這個狀態碼來判斷接口返回的結果是什么類型的數據。可是我們的后臺人員給的卻是response.code()是200,然后給了一個{"code",200,"":"needlogin"}
這樣的一個字符串,那么問題就來了
我項目中并沒有對網絡模塊對返回結果進行二次封裝。所以在處理這個問題的時候比較麻煩,是用了OKhttp的自定義攔截器,代碼如下
OkHttpClient.Builder builder =new OkHttpClient.Builder();
// 設置超時
builder.connectTimeout(TIMEOUT, TimeUnit.SECONDS);
builder.readTimeout(TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(TIMEOUT, TimeUnit.SECONDS);
builder.addNetworkInterceptor(new LoginInterceptor());
OkHttpClient client = builder.retryOnConnectionFailure(false)
.build();
mRetrofit =new Retrofit.Builder()
// 設置請求的域名
.baseUrl(BASE_URL)
// 設置解析轉換工廠,用自己定義的
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
//...
public class LoginInterceptorimplements Interceptor {
@Override
public Response intercept(@NonNull Chain chain)throws IOException {
// 攔截請求,獲取到該次請求的request
Request request = chain.request();
// 執行本次網絡請求操作,返回response信息
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
String result ="";
if (responseBody !=null){
// 包含需要登錄
result = response.body().string();
if(result.contains("needlogin")) {
EventBus.getDefault().post(new MessageEvent(Config.NEED_LOGIN));
}
}
return response;
}
}
然后在接口返回的response中再去取response.body().string()會報IllegalArgumentException:closed
這樣的錯誤。手動問號三連:???為什么接口數據都不行了,慌了
各種百度,發現response.body().string()只能調用一次,以前在debug的時候有發現過,但是沒重視,現在終于要面對這個問題了
分析這個問題,看源碼
首先,看ResponseBody
類中的方法 string()
public final String string() throws IOException {
BufferedSource source = source();
try {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
} finally {
Util.closeQuietly(source); // 注意這一行,這行是要關閉什么東西
}
}
接下來在看Util.closeQuietly(source)
,看起來是關閉什么資源
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}
public void close() throws IOException;
能看到這個close()
的注釋,關閉此流并釋放與之關聯的任何系統資源。
到這個地方似乎明白了什么,當調用response.body().string()
這個方法的時候,這個response
的資源就會被釋放,導致不能重復使用。
那么怎么解決呢
如果一定要重復使用response.body().string()
,該怎么做呢,而這個response
的資源又被釋放了。response
里面的資源被釋放了,那就重新創建一個于是有了response.newBuilder().body(ResponseBody.create(null,result)).code(200).build()
,重新創建response
的時候,需要重新加入資源code
和ResponseBody
,其中ResponseBody.create()
方法,中的兩個參數
public static ResponseBody create(@Nullable MediaType contentType, String content) {
Charset charset = UTF_8;
if (contentType != null) {
charset = contentType.charset();
if (charset == null) {
charset = UTF_8;
contentType = MediaType.parse(contentType + "; charset=utf-8");
}
}
Buffer buffer = new Buffer().writeString(content, charset);
return create(contentType, buffer.size(), buffer);
}
可以看出這個第一個參數可以是null,默認是utf-8格式,第二個參數是response.body().string()
這個字符串。重新加入的code
就是原來的response.code()
,這樣返回就OK了