当我尝试记录网络响应时,OKHttp抛出非法状态异常

时间:2016-07-28 16:07:57

标签: java android retrofit okhttp

我在我的OkHttp客户端上放了以下拦截器:

httpClient.addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        Log.d("Response", response.body().string());
        return response;
    }
    });

然而,这对于Retrofit 2来说并不好看。看起来你只能从响应中读取一次流,这可能是造成异常的原因。我在想改造试图解析日志已经解析过的流。那我如何得到回应呢?我目前正在尝试调试一个非常讨厌和奇怪的格式错误的json异常。

这是异常堆栈跟踪:

07 - 28 10: 58: 21.575 22401 - 22529 / REDACTED E / AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: REDACTED, PID: 22401
    java.lang.IllegalStateException: closed
    at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java: 378)
    at okio.Buffer.writeAll(Buffer.java: 956)
    at okio.RealBufferedSource.readByteArray(RealBufferedSource.java: 92)
    at okhttp3.ResponseBody.bytes(ResponseBody.java: 83)
    at okhttp3.ResponseBody.string(ResponseBody.java: 109)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 90)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java: 160)
    at okhttp3.RealCall.access$100(RealCall.java: 30)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java: 127)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java: 32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java: 1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java: 587)
    at java.lang.Thread.run(Thread.java: 841)

我看到堆栈中有多个拦截器,但我只是明确地添加了一个拦截器,这就是抛出异常的。

6 个答案:

答案 0 :(得分:28)

You are consuming the response body in the interceptor, so you're going to want to create a new response:

@Override public Response intercept(Chain chain) throws IOException {
  Response response = chain.proceed(chain.request());
  ResponseBody body = response.body();
  String bodyString = body.string();
  MediaType contentType = body.contentType();
  Log.d("Response", bodyString);
  return response.newBuilder().body(ResponseBody.create(contentType, bodyString)).build();
}

You may also want to check out the logging interceptor in OkHttp's repo: https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor

答案 1 :(得分:16)

不要多次调用响应主体,因为它被读取为流而不存储在内存中。

  

也许你不止一次地调用response.body()。string(),因为响应体可能很大,所以OkHttp不会将它存储在内存中,它会在需要时将其作为来自网络的流读取。

     

当你读取body作为字符串时()OkHttp下载响应体并将其返回给你而不保留对字符串的引用,没有新的请求就无法下载两次。

https://github.com/square/okhttp/issues/1240

答案 2 :(得分:3)


由于尝试转换响应正文 2X,我也遇到了此异常。 首先,我记录了 response.body()。string(),然后再次将其传递给方法。 这是造成异常的原因。 检查响应主体是否多次转换为字符串。

答案 3 :(得分:0)

错误很奇怪,但我看到的第一个问题是您正在使用OKHTTP3并尝试将构建器添加到已创建的客户端。

OkHttpClient 现在是不可变的,您必须通过调用 addInterceptor <将拦截器直接添加到 OkHttpClient.Builder 构建器上的/ em>方法。

有关如何执行此操作的更清晰,请参阅此Github Issue。我认为这应该可以解决你的问题。

答案 4 :(得分:0)

响应主体可能很大,因此OkHttp不会将其存储在内存中,而是在需要时将其作为网络流读取。

当您将body读取为字符串()时,OkHttp将下载响应正文并在不保留对该字符串的引用的情况下将其返回给您,因此,如果没有新的请求,则无法将其下载两次。

答案 5 :(得分:0)

尝试上传文件(图像)并使用Chrome DevTools(Stetho)时出现此错误。

关闭Chrome DevTools解决了我的问题