studio okhttpclient library android okhttp

android - okhttpclient - El acceso a la cadena del cuerpo de una respuesta OkHttp da como resultado dos veces la excepción IllegalStateException: cerrado



okhttp vs retrofit (5)

Implemento mis llamadas http a través de la biblioteca OkHttp. Todo funciona bien, pero me di cuenta de que, cuando IllegalStateException al cuerpo como una cadena de respuesta dos veces, se IllegalStateException una IllegalStateException . Es decir, hago (por ejemplo): Log.d("TAG", response.body().string()) y luego quiero usar esa cadena como processResponse(response.body().string()) . Pero esa segunda llamada lanza la excepción con el mensaje closed .

¿Cómo puede ser posible que acceder a una cadena dos veces resulte en un error? Quiero procesar esa respuesta sin la necesidad de agregar un objeto envoltorio / ficticio solo para guardar algunos valores (como encabezado, cuerpo, código de estado).


Ampliando un poco las respuestas de user2011622 y Greg Ennis , creé un método que te ayuda a crear un clon completo del cuerpo, permitiéndote consumir cada copia del cuerpo por separado. Yo uso este método con 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); }


El método de string en la respuesta leerá el flujo de entrada (red) y lo convertirá en una cadena. Así que construye dinámicamente la cadena y te la devuelve. La segunda vez que lo llama, el flujo de red ya se ha consumido y ya no está disponible.

Debería guardar el resultado de la string en una variable de cadena y luego accederla tantas veces como sea necesario.


Para una explicación del problema, vea la respuesta de Greg Ennis .

Sin embargo, si no puede pasar fácilmente el resultado en una variable, pero aún necesita acceder al cuerpo de la respuesta dos veces, tiene otra opción:

Clona el buffer antes de leerlo. Por eso el búfer original no se vacía ni se cierra. Ver este fragmento de código:

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

Este enfoque se utiliza en HttpLoggingInterceptor en el proyecto okhttp por cuadratura.


Por cierto, además de la respuesta de Greg Ennis, puedo decir que una vez me sucedió esto cuando olvidé response.body (). String () en la ventana del reloj. Así que debajo del depurador el cuerpo estaba leyendo en el reloj y la secuencia de la red se cerró después de eso.


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