OkHttp踩坑记:为何 response,IBM大面积辞退40岁+的员工

});


在 `onResponse()` 中,为便于调试,我打印了返回体,然后通过 `parseResponseStr()` 方法解析返回体(注意:这儿两次调用了 `response.body().string()`)。

这段看起来没有任何问题的代码,实际运行后却出了问题:通过控制台看到成功打印了返回体数据(json),但紧接着抛出了异常:

java.lang.IllegalStateException: closed


2.解决问题
------

检查代码后,发现问题出在调用 `parseResponseStr()` 时,再次使用了 `response.body().string()` 作为参数。由于当时赶时间,上网查阅后发现 `response.body().string()` 只能调用一次,于是修改 `onResponse()` 方法中的逻辑后解决了问题:

getHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}

@Override
public void onResponse(Call call, Response response) throws IOException {
    //此处,先将响应体保存到内存中
    String responseStr = response.body().string();
    if (BuildConfig.DEBUG) {
        Log.d(TAG, "onResponse: " + responseStr);
    }
    //解析请求体
    parseReponseStr(responseStr);
}

});


3.结合源码分析问题
----------

问题解决了,事后还是要分析的。由于之前对 `OkHttp` 的了解仅限于使用,没有仔细分析过其内部实现的细节,周末抽时间往下看了看,算是弄明白了问题发生的原因。

先分析最直观的问题:**为何 `response.body().string()` 只能调用一次?**

拆解来看,先通过 `response.body()` 得到 `ResponseBody` 对象(其是一个抽象类,在此我们不需要关心具体的实现类),然后调用 `ResponseBody` 的 `string()` 方法得到响应体的内容。

分析后 `body()` 方法没有问题,我们往下看 `string()` 方法:

public final String string() throws IOException {
return new String(bytes(), charset().name());
}


很简单,通过指定字符集(charset)将 `byte()` 方法返回的 `byte[]` 数组转为 `String` 对象,构造没有问题,继续往下看 `byte()` 方法:

public final byte[] bytes() throws IOException {
//…
BufferedSource source = source();
byte[] bytes;
try {
bytes = source.readByteArray();
} finally {
Util.closeQuietly(source);
}
//…
return bytes;
}


`//...` 表示删减了无关代码,下同。

在 `byte()` 方法中,通过 `BufferedSource` 接口对象读取 `byte[]` 数组并返回。结合上面提到的异常,我注意到 `finally` 代码块中的 `Util.closeQuietly()` 方法。excuse me?默默地关闭???

![](https://user-gold-cdn.xitu.io/2018/1/8/160d188374f8bf5e?imageView2/0/w/1280/h/960/ignore-error/1)

这个方法看起来很诡异有木有,跟进去看看:

public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}


原来,上面提到的 `BufferedSource` 接口,根据代码文档注释,可以理解为 **资源缓冲区**,其实现了 `Closeable` 接口,通过复写 `close()` 方法来 **关闭并释放资源**。接着往下看 `close()` 方法做了什么(在当前场景下,`BufferedSource` 实现类为 `RealBufferedSource`):

//持有的 Source 对象
public final Source source;

@Override
public void close() throws IOException {
if (closed) return;
closed = true;
source.close();
buffer.clear();
}


很明显,通过 `source.close()` 关闭并释放资源。说到这儿, `closeQuietly()` 方法的作用就不言而喻了,就是关闭 `ResponseBody` 子类所持有的 `BufferedSource` 接口对象。

分析至此,我们恍然大悟:当我们第一次调用 `response.body().string()` 时,OkHttp 将响应体的缓冲资源返回的同时,调用 `closeQuietly()` 方法默默释放了资源。

如此一来,当我们再次调用 `string()` 方法时,依然回到上面的 `byte()` 方法,这一次问题就出在了 `bytes = source.readByteArray()` 这行代码。一起来看看 `RealBufferedSource` 的 `readByteArray()` 方法:

@Override
public byte[] readByteArray() throws IOException {
buffer.writeAll(source);
return buffer.readByteArray();
}


继续往下看 `writeAll()` 方法:

@Override
public long writeAll(Source source) throws IOException {
//…
long totalBytesRead = 0;
for (long readCount; (readCount = source.read(this, Segment.SIZE)) != -1; ) {
totalBytesRead += readCount;
}
return totalBytesRead;
}


问题出在 `for` 循环的 `source.read()` 这儿。还记得在上面分析 `close()` 方法时,其调用了 `source.close()` 来关闭并释放资源。那么,再次调用 `read()` 方法会发生什么呢:

@Override
public long read(Buffer sink, long byteCount) throws IOException {
//…
if (closed) throw new IllegalStateException(“closed”);
//…
return buffer.read(sink, toRead);
}


至此,与我在前面遇到的崩溃对上了:

java.lang.IllegalStateException: closed


4.OkHttp 为什么要这么设计?
------------------

## Android核心知识点

**面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验。**

下面这份PDF是我翻阅了差不多3个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些**Android的核心知识点**,全部都是精华中的精华,我能面试到现在2-2资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

![](https://img-blog.csdnimg.cn/img_convert/5f249d15ea84fc3a3aa122afe7cd4740.png)

**不管是Android基础还是Java基础以及常见的数据结构,这些是无原则地必须要熟练掌握的**,尤其是非计算机专业的同学,面试官一上来肯定是问你基础,要是基础表现不好很容易被扣上基础不扎实的帽子,常见的就那些,只要你平时认真思考过基本上面试是没太大问题的。

最后为了帮助大家深刻理解**Android相关知识点的原理以及面试相关知识**,这里放上我搜集整理的**2019-2021BAT 面试真题解析**,我把大厂面试中**常被问到的技术点**整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

试是没太大问题的。

最后为了帮助大家深刻理解**Android相关知识点的原理以及面试相关知识**,这里放上我搜集整理的**2019-2021BAT 面试真题解析**,我把大厂面试中**常被问到的技术点**整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值