ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OkHttp踩坑记:为何 response,看这里

2021-09-08 12:02:34  阅读:268  来源: 互联网

标签:body 坑记 bytes Response source OkHttp close byte response


很简单,通过指定字符集(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?默默地关闭???

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eqJdELJs-1631073220804)(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() 这行代码。一起来看看 RealBufferedSourcereadByteArray() 方法:

@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 为什么要这么设计?

通过 fuc*ing the source code,我们找到了问题的根本,但我还有一个疑问:OkHttp 为什么要这么设计

其实,理解这个问题最好的方式就是查看 ResponseBody 的注释文档,正如 JakeWhartonissues 中给出的回复:

reply of JakeWharton in okhttp issues

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fafi1pi0-1631073220807)(https://user-gold-cdn.xitu.io/2018/1/8/160d18888f98cd6a?imageView2/0/w/1280/h/960/ignore-error/1)]

就简单的一句话:It's documented on ResponseBody. 于是我跑去看类注释文档,最后梳理如下:

在实际开发中,响应主体 RessponseBody 持有的资源可能会很大,所以 OkHttp 并不会将其直接保存到内存中,只是持有数据流连接。只有当我们需要时,才会从服务器获取数据并返回。同时,考虑到应用重复读取数据的可能性很小,所以将其设计为一次性流(one-shot),读取后即 ‘关闭并释放资源’

5.总结

最后,总结以下几点注意事项,划重点了:

  1. 响应体只能被使用一次;
  2. 响应体必须关闭:值得注意的是,在下载文件等场景下,当你以 response.body().byteStream() 形式获取输入流时,务必通过 Response.close() 来手动关闭响应体。
  3. 获取响应体数据的方法:使用 bytes()string() 将整个响应读入内存;或者使用 source(), byteStream(), charStream() 方法以流的形式传输数据。
  4. 以下方法会触发关闭响应体:
Response.close()
Response.body().close()
Response.body().source().close()
Response.body().charStream().close()
Response.body().byteString().close()
Response.body().bytes()
ponse.body().source().close()
Response.body().charStream().close()
Response.body().byteString().close()
Response.body().bytes()
 

标签:body,坑记,bytes,Response,source,OkHttp,close,byte,response
来源: https://blog.csdn.net/m0_61072577/article/details/120176971

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有