我们在使用OKHttp做网络请求的时候,会发现一个奇怪的现象,那就是Response对象的这个方法:
Response response = client.newCall(requestBuilder.build()).execute();
String s = response.body().string();
只能调用一次,多次调用的时候,会抛出IllegalStateException的异常,这时为什么呢?我们且从源码角度进行分析。
首先是他的前半部分response.body()方法,body()方法的源码如下:
public @Nullable ResponseBody body() {
return body;
}
可以看出body()方法内部除了返回一个body对象外,并没有做其他的任何处理,理论上这里不存在抛出任何异常的可能。
我们接着往下看他的下一个方法:
response.body().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);
}
}
这里,整个方法都是围绕着source 这个变量展开的。可以确定他是这个方法的核心,也是我们要分析的一个关键。
第二行代码很简单,就是获得一个source 对象。继续看第四行代码:
Charset charset = Util.bomAwareCharset(source, charset());
就是将我们的source对象转化为char字符串,源码如下:
public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException {
if (source.rangeEquals(0, UTF_8_BOM)) {
source.skip(UTF_8_BOM.size());
return UTF_8;
}
if (source.rangeEquals(0, UTF_16_BE_BOM)) {
source.skip(UTF_16_BE_BOM.size());
return UTF_16_BE;
}
if (source.rangeEquals(0, UTF_16_LE_BOM)) {
source.skip(UTF_16_LE_BOM.size());
return UTF_16_LE;
}
if (source.rangeEquals(0, UTF_32_BE_BOM)) {
source.skip(UTF_32_BE_BOM.size());
return UTF_32_BE;
}
if (source.rangeEquals(0, UTF_32_LE_BOM)) {
source.skip(UTF_32_LE_BOM.size());
return UTF_32_LE;
}
return charset;
}
这里面没有什么特别的操作,也没发现值得特别关注的地方。
我们再看最后可能出现跑异常的地方:finally关键字里面的部分:
Util.closeQuietly(source);
源码如下:
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}
看到这里,不知道你有没有发现什么关键的信息,反正笔者是瞬间注意到了这个关键的代码:
closeable.close();
也就是说,我们在调用一次response.body().string();后,Response就把closeable给关闭了;当我们再次调用这个方法的时候,closeable为空值,就如数据库里面consor游标指针被关闭了一样。之所以要关闭它主要是为了防止无用的多余的I/O操作浪费资源,防止内存泄漏;
因此response.body().string()方法只能调用一次。
这里要注意下,我们讲的是string() 方法,是string() 方法,是string() 方法,大家不要误解为
toString()方法,将对象转化为String字符串的toString方法陷入不存在前述存在的问题。这里我们不多讲了。