Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 两次访问OkHttp响应的主体字符串将导致IllegalStateException:closed_Android_Okhttp - Fatal编程技术网

Android 两次访问OkHttp响应的主体字符串将导致IllegalStateException:closed

Android 两次访问OkHttp响应的主体字符串将导致IllegalStateException:closed,android,okhttp,Android,Okhttp,我通过OkHttp库实现我的http调用。一切正常,但我注意到,当我以响应字符串的形式访问正文两次时,将抛出一个IllegalStateException。 也就是说,我会这样做(例如):Log.d(“TAG”,response.body().string())之后,我实际上想使用这个字符串,比如processResponse(response.body().string())。但是第二个调用抛出异常,并显示消息closed 两次访问字符串怎么可能导致失败?我希望处理该响应,而无需添加包装器/虚

我通过OkHttp库实现我的http调用。一切正常,但我注意到,当我以响应字符串的形式访问正文两次时,将抛出一个
IllegalStateException
。 也就是说,我会这样做(例如):
Log.d(“TAG”,response.body().string())
之后,我实际上想使用这个字符串,比如
processResponse(response.body().string())
。但是第二个调用抛出异常,并显示消息
closed


两次访问字符串怎么可能导致失败?我希望处理该响应,而无需添加包装器/虚拟对象,仅用于保存一些值(如header、body、statuscode)。

响应上的
string
方法将读取输入(网络)流并将其转换为字符串。因此,它动态构建字符串并将其返回给您。第二次调用时,网络流已被占用,不再可用


您应该将
字符串的结果保存到字符串变量中,然后根据需要多次访问它。

更新:

因为现在有一个更简单、更轻量级的API():

原始答案:

有关此问题的解释,请参见

但是,如果您不能轻松地将结果传递到变量中,但仍然需要访问响应正文两次,则您还有另一个选择:

在读取缓冲区之前克隆它。至此,原始缓冲区既不清空也不关闭。请参阅此代码段:

ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // request the entire body.
Buffer buffer = source.buffer();
// clone buffer before reading from it
String responseBodyString = buffer.clone().readString(Charset.forName("UTF-8"))
Log.d("TAG", responseBodyString);

square自己在项目中也使用了这种方法。

顺便说一句,除了Greg Ennis的答案,我还可以告诉你,当我忘记了watch窗口中的response.body().string()时,我曾经遇到过什么情况。因此,在调试器下,主体正在读取手表中的数据,然后网络流被关闭。

对和的答案进行了一些扩展,我创建了一个方法,帮助您创建主体的完整克隆,允许您单独使用主体的每个副本。我自己用这种方法来改造2

/**
 * Clones a raw buffer so as not to consume the original
 * @param rawResponse the original {@link okhttp3.Response} as returned
 *                    by {@link Response#raw()}
 * @return a cloned {@link ResponseBody}
 */
private ResponseBody cloneResponseBody(okhttp3.Response rawResponse) {
    final ResponseBody responseBody = rawResponse.body();
    final Buffer bufferClone = responseBody.source().buffer().clone();
    return ResponseBody.create(responseBody.contentType(), responseBody.contentLength(), bufferClone);
}

您可以调用
response.peekBody(Long.MAX_值)
将整个响应缓冲在内存中,并获取它的轻量级副本。这将大大减少浪费


此代码获取响应主体,不会使用缓冲区。这是在

中添加的一个新api,我希望可以重用
响应
对象。在使用OkHttp之前,我返回了一个自定义的ApiResult对象,我在其中保存了所有相关信息,但它只是一个重复的IMO。也许你应该在github上签出它。我明白你的意思。我猜他们只是想尽可能地保持低级别,而不缓存可能永远不会调用的结果again@penkzhou这个问题只是说你不能用它两次。没有进一步的消息。但是,还是要感谢将其保存为InputStream如何?只需调用带有响应的方法,您将获得响应正文的另一个副本,但原始副本将保持可用(打开)查看源代码并实现与先前相同的内容(来自克隆源代码的新信息),我对它不太熟悉。真的需要这种方式,否则我需要在20-30个地方进行更改:)谢谢!!工作得很好。
/**
 * Clones a raw buffer so as not to consume the original
 * @param rawResponse the original {@link okhttp3.Response} as returned
 *                    by {@link Response#raw()}
 * @return a cloned {@link ResponseBody}
 */
private ResponseBody cloneResponseBody(okhttp3.Response rawResponse) {
    final ResponseBody responseBody = rawResponse.body();
    final Buffer bufferClone = responseBody.source().buffer().clone();
    return ResponseBody.create(responseBody.contentType(), responseBody.contentLength(), bufferClone);
}
ResponseBody body = response.peekBody(Long.MAX_VALUE);
String content = body.string();
//do something