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. Кажется, что вы можете только прочитать поток из ответа один раз, и это может быть причиной исключения. Я думаю, что retrofit пытается разобрать поток, который уже проанализировал журнал. Как же я получу ответ? В настоящее время я пытаюсь отладить очень неприятное и странное отклонение от 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)

Я вижу, что в стеке есть несколько перехватчиков, но я только когда-либо добавляю один, который является тем, что выбрасывает исключение.

Ответ 1

Вы потребляете тело ответа в перехватчике, поэтому вам нужно создать новый ответ:

@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();
}

Вы также можете проверить регистрационный перехватчик в репозитории OkHttp: https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor

Ответ 2

Не вызывайте тело ответа более одного раза, так как оно читается как поток и не сохраняется в памяти.

Возможно, вы вызываете response.body(). String() более одного раза, потому что тело ответа может быть огромным, поэтому OkHttp не сохраняет его в памяти, он читает его как поток из сети, когда вам это нужно.

Когда вы читаете тело как строку(), OkHttp загружает тело ответа и возвращает его вам, не сохраняя ссылки на строку, его нельзя загрузить дважды без нового запроса.

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

Ответ 3


Я также получил это исключение из-за попытки преобразования тела ответа 2X. Сначала я зарегистрировал response.body(). String(), а затем снова передал то же самое методу. Который был причиной исключения. Проверьте, преобразовано ли тело ответа в строку более одного раза.

Ответ 4

Ошибка является странной, но первая проблема, которую я вижу, заключается в том, что вы используете OKHTTP3 и пытаетесь добавить создателя к уже созданному клиенту.

OkHttpClient теперь неизменный, и вам нужно будет добавить перехватчики непосредственно в OkHttpClient.Builder, вызвав метод addInterceptor в построителе.

Для большей ясности о том, как это сделать, см. этот Github Issue. Я думаю, что это должно решить вашу проблему.

Ответ 5

Тело ответа может быть огромным, поэтому OkHttp не сохраняет его в памяти, он читает его как поток из сети, когда вам это нужно.

Когда вы читаете тело как строку(), OkHttp загружает тело ответа и возвращает его вам, не сохраняя ссылки на строку, его нельзя загрузить дважды без нового запроса.