我通过OkHttp库实现我的http调用。一切正常,但我注意到,当我作为响应的字符串访问身体两次时,将抛出IllegalStateException
。
也就是说,我(例如):Log.d("TAG", response.body().string())
之后我真的想要使用像processResponse(response.body().string())
那样的字符串。但是第二次调用会抛出消息closed
的异常。
两次访问字符串怎么可能导致失败?我想处理该响应而不需要添加包装器/虚拟对象只是为了保存一些值(如header,body,statuscode)。
答案 0 :(得分:54)
<强>更新强>
作为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中用于平方。
答案 1 :(得分:35)
响应上的string
方法将读取输入(网络)流并将其转换为字符串。所以它动态构建字符串并将其返回给您。第二次调用它时,网络流已经被消耗并且不再可用。
您应该将string
的结果保存到String变量中,然后根据需要多次访问它。
答案 2 :(得分:3)
答案 3 :(得分:1)
您可以调用response.peekBody(Long.MAX_VALUE);
将整个响应缓冲在内存中并获得其轻量级副本。这样会减少浪费。 See this issue on GitHub。
答案 4 :(得分:1)
ResponseBody body = response.peekBody(Long.MAX_VALUE);
String content = body.string();
//do something
此代码获取响应主体,并且不会占用缓冲区。它是this issue
中添加的新API答案 5 :(得分:0)
在user2011622和Greg Ennis的答案上稍微扩展一下,我创建了一个方法,可以帮助您创建一个完整的正文克隆,允许您分别使用正文的每个副本。我自己使用这种方法和Retrofit2
Viewbox
答案 6 :(得分:-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();
}
}