web-dev-qa-db-ja.com

ネットワーク応答をログに記録しようとすると、不正な状態の例外をスローする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でNiceを再生していません。応答からストリームを読み取ることができるのは1回だけであり、それが例外の原因である可能性があります。レトロフィットは、ログがすでに解析したストリームを解析しようとしていると思います。どうすれば応答を取得できますか?現在、非常に厄介で奇妙な不正な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)

スタックには複数のインターセプターがあることがわかりますが、明示的に追加するのは例外のみです。

インターセプターで応答本文を使用しているため、新しい応答を作成する必要があります。

@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

29
Eric Cochran

応答本体はストリームとして読み取られ、メモリに保存されないため、応答本体を複数回呼び出さないでください。

応答本文は非常に大きくなる可能性があり、OkHttpはそれをメモリに保存せず、必要なときにネットワークからストリームとして読み取るため、response.body()。string()を複数回呼び出している可能性があります。

本文をstring()として読み取る場合、OkHttpは応答本文をダウンロードし、文字列への参照を保持せずにそれを返します。新しいリクエストなしで2回ダウンロードすることはできません。

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

19
user4981235

また、応答本体を変換しようとしたため、この例外が発生しました2X。最初にログを記録しましたresponse.body()。string()方法。これが例外の原因でした。応答本文が文字列に複数回変換されているかどうかを確認してください。

4
Gaurav Pandit

エラーは奇妙ですが、私が見る最初の問題は、OKHTTP3を使用していて、既に作成されたクライアントにビルダーを追加しようとしていることです。

OkHttpClientimmutableであり、インターセプターを直接追加する必要がありますビルダーでaddInterceptorメソッドを呼び出してOkHttpClient.Builderに.

これを行う方法の詳細については、こちらをご覧ください Github Issue 。これで問題を解決できると思います。

0
Aashrai Ravooru

応答本文は巨大になる可能性があるため、OkHttpはメモリに保存せず、必要なときにネットワークからストリームとして読み取ります。

本文をstring()として読み取る場合、OkHttpは応答本文をダウンロードし、文字列への参照を保持せずにそれを返します。新しいリクエストなしで2回ダウンロードすることはできません。

0
Dhananjay Kumar