OkHttp踩坑随笔为何 response.body().string() 只能调用一(2)

分析后 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?默默地关闭???

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

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;
}
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/296.html