访问OkHttp Response的正文字符串两次会导致IllegalStateException:closed

问题描述 投票:34回答:7

我通过OkHttp库实现我的http调用。一切都很好,但我注意到,当我作为一个响应的字符串两次访问身体时,将抛出IllegalStateException。也就是说,我(例如):Log.d("TAG", response.body().string()),之后我实际上想要使用像processResponse(response.body().string())那样的字符串。但是第二次调用抛出了closed消息的异常。

怎么可能两次访问字符串导致失败?我想处理该响应而不需要添加包装器/虚拟对象只是为了保存一些值(如header,body,statuscode)。

android okhttp
7个回答
36
投票

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

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


56
投票

更新:

作为mugwort points out,现在有一个更简单,更轻量级的API(see GitHub issue):

String responseBodyString = response.peekBody(Long.MAX_VALUE).string();
Log.d("TAG", responseBodyString);

原答案:

有关问题的解释,请参阅Greg Ennis' answer

但是,如果您无法轻松地将结果传递给变量,但仍需要访问响应主体两次,则还有另一个选项:

在读取之前克隆缓冲区。由此原始缓冲区既不清空也不关闭。看到这个片段:

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

这种方法在HttpLoggingInterceptor项目okhttp中被广场本身使用。


3
投票

顺便说一句,除了格雷格恩尼斯的回答之外,当我在观察窗口忘记了response.body()。string()时,我可以告诉我发生了什么事。因此,在调试器下,正在读取监视器,然后关闭网络流。


1
投票

您可以调用response.peekBody(Long.MAX_VALUE);来缓冲内存中的整个响应,并获得它的轻量级副本。它会减少浪费。 See this issue on GitHub


1
投票
ResponseBody body = response.peekBody(Long.MAX_VALUE);
String content = body.string();
//do something

此代码获取响应正文,不会消耗缓冲区。这是在this issue添加的新api


0
投票

user2011622Greg Ennis的答案上稍微扩展一下,我创建了一个方法,可以帮助您创建一个完整的身体克隆,允许您分别使用身体的每个副本。我自己用Retrofit2这个方法

/**
 * 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);
}

-1
投票
            if (response.isSuccessful()) {

                Gson gson = new Gson();
                String successResponse = gson.toJson(response.body());
                Log.d(LOG_TAG, "successResponse: " + successResponse);

            } else {

                try {
                    if (null != response.errorBody()) {
                        String errorResponse = response.errorBody().string();
                        Log.d(LOG_TAG, "errorResponse: " + errorResponse);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            } 
© www.soinside.com 2019 - 2024. All rights reserved.