response.body().string()的猫腻…
在一段从服务器获取数据的代码中,刚开始我是如此写的:
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
try {
if (response.isSuccessful()) {
if (response.body().string().equals("")) {
//...
} else {
tv.setText(response.body().string());
//...
}
} else {
//... }
} catch (Exception e) {
e.printStackTrace();
//...
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//...
}
});
第一眼看上去似乎没有毛病,如果响应成功,通过“response.body().string().equals(“”)”判断当前服务器返回的string内容是否为空,若为空则执行相应操作,若不为空则将“response.body().string()”的内容显示在一个TextView上。运行之后发现死活都没有数据显示出来,于是debug,发现两个if都会走,而response.body().string()在set到TextView那一步却变成空了…说明response.body().string()获取的字符串,在第二次调用时丢失了内容。
查阅相关源码后发现事实确实如此,并非bug。OkHttp请求回调中response.body().string()只能有效调用一次!
Okhttp源码中的ResponseBody.java文件的string()函数是这么写的:
/**
* Returns the response as a string decoded with the charset of the Content-Type header. If that
* header is either absent or lacks a charset, this will attempt to decode the response body in
* accordance to <a href="https://en.wikipedia.org/wiki/Byte_order_mark">its BOM</a> or UTF-8.
* Closes {@link ResponseBody} automatically.
*
* <p>This method loads entire response body into memory. If the response body is very large this
* may trigger an {@link OutOfMemoryError}. Prefer to stream the response body if this is a
* possibility for your response.
*/
public final String string() throws IOException {
BufferedSource source = source();
try {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
} finally {
Util.closeQuietly(source);
}
}
可以看到执行完string()方法后,IO流通过这一个语句被关闭了:Util.closeQuietly(source);
所以,OkHttp请求回调中response.body().string()只能有效调用一次!切记!文章开头的代码改成下面的写法则一切正常:
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
try {
if (response.isSuccessful()) {
String result = response.body().string();
if (result.equals("")) {
//...
} else {
tv.setText(result);
//...
}
} else {
//...
}
} catch (Exception e) {
e.printStackTrace();
//...
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//...
}
});