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.
Peter F
Aktualisieren:
Wie Beifuß hervorhebt, ist jetzt eine einfachere, leichtere API verfügbar (siehe GitHub-Problem):
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);
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
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.
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.
11755500cookie-checkDer zweimalige Zugriff auf die Textzeichenfolge einer OkHttp-Antwort führt zu IllegalStateException: closedyes