Der zweimalige Zugriff auf die Textzeichenfolge einer OkHttp-Antwort führt zu IllegalStateException: closed

Lesezeit: 3 Minuten

Ich implementiere meine http-Aufrufe über die OkHttp-Bibliothek. Alles funktioniert einwandfrei, aber das ist mir aufgefallen, als ich zweimal auf den Body als String der Antwort zugreife IllegalStateException wird geworfen. Das heißt, ich mache (zum Beispiel): Log.d("TAG", response.body().string()) und danach möchte ich diese Zeichenfolge gerne verwenden processResponse(response.body().string()). Aber dieser zweite Aufruf löst die Ausnahme mit der Nachricht aus closed.

Wie kann es möglich sein, dass der zweimalige Zugriff auf einen String zu einem Fehler führt? Ich möchte diese Antwort verarbeiten, ohne dass ein Wrapper/Dummy-Objekt hinzugefügt werden muss, nur um einige Werte (wie Header, Body, Statuscode) zu speichern.

Benutzer-Avatar
Peter F

Aktualisieren:

Wie Beifuß hervorhebt, ist jetzt eine einfachere, leichtere API verfügbar (siehe GitHub-Problem):

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

Ursprüngliche Antwort:

Eine Erklärung des Problems finden Sie in der Antwort von Greg Ennis.

Wenn Sie das Ergebnis jedoch nicht einfach in einer Variablen übergeben können, aber dennoch zweimal auf den Antworttext zugreifen müssen, haben Sie eine andere Möglichkeit:

Klonen Sie den Puffer, bevor Sie ihn lesen. Dadurch wird der Originalpuffer weder geleert noch geschlossen. Siehe diesen Ausschnitt:

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

Dieser Ansatz wird in verwendet HttpLoggingInterceptor im Projekt okhttp durch Quadrat selbst.

  • vielen Dank Alter. Wirklich gebraucht, sonst hätte ich es an 20-30 Stellen ändern müssen 🙂

    – Syed Muhammed Oan

    16. August 2017 um 8:41 Uhr

Das string -Methode in der Antwort liest den Eingabestrom (Netzwerk) und konvertiert ihn in eine Zeichenfolge. Es baut also dynamisch die Zeichenfolge auf und gibt sie an Sie zurück. Beim zweiten Aufruf ist der Netzwerkstream bereits verbraucht und steht nicht mehr zur Verfügung.

Sie sollten das Ergebnis von speichern string in eine String-Variable und greifen Sie dann so oft wie nötig darauf zu.

  • Ich hatte gehofft, dass ich das einfach wiederverwenden könnte Response Objekt. Vor der Verwendung von OkHttp habe ich ein benutzerdefiniertes ApiResult-Objekt zurückgegeben, in dem ich nur alle relevanten Informationen gespeichert habe, aber es ist IMO nur ein Duplikat.

    – degill

    13. Januar 2015 um 13:03 Uhr

  • vielleicht solltest du dir das mal anschauen Ausgabe auf github.

    – penkzhou

    13. Januar 2015 um 13:04 Uhr

  • Ich weiß, worauf du hinauswillst. Ich denke, sie wollten es nur so niedrig wie möglich halten, ohne das Ergebnis zwischenzuspeichern, das möglicherweise nie wieder aufgerufen wird

    – Greg Ennis

    13. Januar 2015 um 13:08 Uhr

  • @penkzhou Nun, dieses Problem besagt nur, dass Sie es nicht zweimal verwenden können. Keine weiteren Informationen. Aber trotzdem danke

    – degill

    13. Januar 2015 um 13:35 Uhr


  • Was ist mit dem Speichern als InputStream?

    – IgorGanapolsky

    10. Juni 2018 um 19:31 Uhr

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

Dieser Code erhält den Antworttext und verbraucht den Puffer nicht. Es ist eine neu hinzugefügte API dieses Problem

  • Dokumentation sagt: “Die meisten Anwendungen sollten eine bescheidene Grenze setzen byteCountz. B. 1 MiB.”

    – arekolek

    29. April um 21:27 Uhr

Benutzer-Avatar
Nick Cardoso

Ich habe die Antworten von user2011622 und Greg Ennis ein wenig erweitert und eine Methode entwickelt, die Ihnen hilft, einen vollständigen Klon des Körpers zu erstellen, sodass Sie jede Kopie des Körpers separat konsumieren können. Ich benutze diese Methode selbst mit 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);
}

Übrigens kann ich zusätzlich zu Greg Ennis ‘Antwort sagen, was mir einmal passiert ist, als ich response.body().string() im Überwachungsfenster vergessen habe. Unter dem Debugger las der Körper also in die Uhr und der Netzwerkstream wurde danach geschlossen.

Benutzer-Avatar
axm__

Du kannst anrufen response.peekBody(Long.MAX_VALUE); um die gesamte Antwort im Speicher zu puffern und eine leichtgewichtige Kopie davon zu erhalten. Es wird viel weniger verschwenderisch sein. Siehe dieses Problem auf GitHub.

1175550cookie-checkDer zweimalige Zugriff auf die Textzeichenfolge einer OkHttp-Antwort führt zu IllegalStateException: closed

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy