首先说问题:在做项目的过程中,需要在在调用接口的时候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了
网友评论