Подтвердить что ты не робот

Доступ к строке тела ответа OkHttp дважды приводит к ошибке IllegalStateException: закрыто

Я реализую свои http-вызовы через библиотеку OkHttp. Все работает отлично, но я заметил, что, когда я получаю доступ к телу в виде строки ответа дважды, будет выбрано IllegalStateException. То есть, я делаю (например): Log.d("TAG", response.body().string()), и после этого я действительно хочу использовать эту строку, например processResponse(response.body().string()). Но этот второй вызов выдает исключение с сообщением closed.

Как возможно, что доступ к строке дважды приводит к ошибке? Я хочу обработать этот ответ без необходимости добавлять объект-оболочку/фиктивный объект только для сохранения некоторых значений (например, заголовка, тела, кода состояния).

4b9b3361

Ответ 1

Метод string в ответе будет читать поток ввода (сети) и преобразовать его в строку. Поэтому он динамически строит строку и возвращает ее вам. Во второй раз, когда вы его вызываете, сетевой поток уже потребляется и больше не доступен.

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

Ответ 2

Обновить:

Как указывает mugwort, теперь доступен более простой API, дружественный к памяти (см. Проблему с GitHub):

String responseBodyString = response.peekBody(Long.MAX_VALUE).string();
Log.d("TAG", responseBodyString);

Оригинальный ответ:

Для объяснения проблемы см. Ответ Грега Энниса.

Однако, если вы не можете легко передать результат в переменную, но вам все равно нужно дважды обратиться к телу ответа, у вас есть другая опция:

Клонируйте буфер перед чтением. При этом исходный буфер не очищается и не закрывается. Смотрите этот фрагмент:

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 самим квадратом.

Ответ 3

Кстати, помимо ответа Грега Энниса, я могу сказать, что случилось, когда это произошло со мной, когда я забыл response.body(). string() в окне просмотра. Таким образом, под отладчиком тело читало часы, и после этого сетевой поток закрывался.

Ответ 4

Вы можете вызвать response.peekBody(Long.MAX_VALUE); буферизовать весь ответ в памяти и получить его облегченную копию. Это будет намного менее расточительно. Смотрите эту проблему на GitHub.

Ответ 5

ResponseBody body = response.peekBody(Long.MAX_VALUE);
String content = body.string();
//do something

этот код получает тело ответа и не использует буфер. это новый API, добавленный в этом выпуске

Ответ 6

Развернувшись немного на ответах user2011622 и Грег Эннис, я создал метод, который поможет вам чтобы создать полный клон тела, позволяя вам потреблять каждую копию тела отдельно. Я сам использую этот метод с 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);
}

Ответ 7

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

            }