Heim Web-Frontend js-Tutorial Warum kann „response.body().string()' nicht mehrmals aufgerufen werden?

Warum kann „response.body().string()' nicht mehrmals aufgerufen werden?

Jun 13, 2018 am 10:31 AM
string

Ich glaube, jeder hat OkHttp verwendet oder ist damit in Kontakt gekommen. Ich bin hier auf eine Falle gestoßen, damit jeder es umgehen kann, wenn er in Zukunft auf ähnliche Probleme stößt >

Nur ​​eine Lösung. Probleme reichen nicht aus. Dieser Artikel konzentriert sich auf die Analyse der Ursache des Problems aus der Perspektive des Quellcodes, der voller nützlicher Informationen ist.

1. Das Problem wurde gefunden

Während der Entwicklung habe ich eine Anfrage initiiert, indem ich das OkHttpClient-Objekt erstellt und es der Warteschlange hinzugefügt habe Der Server hat geantwortet: Die Callback-Schnittstelle löst die Methode onResponse() aus und verwendet dann das Response-Objekt, um die Rückgabeergebnisse zu verarbeiten und Geschäftslogik in dieser Methode zu implementieren. Der Code sieht ungefähr wie folgt aus:

//注:为聚焦问题,删除了无关代码
getHttpClient().newCall(request).enqueue(new Callback() {
  @Override
  public void onFailure(Call call, IOException e) {}
  @Override
  public void onResponse(Call call, Response response) throws IOException {
    if (BuildConfig.DEBUG) {
      Log.d(TAG, "onResponse: " + response.body().toString());
    }
    //解析请求体
    parseResponseStr(response.body().string());
  }
});
Nach dem Login kopieren

In onResponse() habe ich zur Vereinfachung des Debuggens den Rückgabetext gedruckt und ihn dann über die Methode parseResponseStr() analysiert (Hinweis: Response.body( ) wird hier zweimal aufgerufen string() ).

Dieser Code, der scheinbar keine Probleme hatte, ging nach der Ausführung tatsächlich schief: Über die Konsole wurde festgestellt, dass die Rückgabedaten (JSON) erfolgreich gedruckt wurden, aber dann wurde eine Ausnahme ausgelöst:

java.lang.IllegalStateException: closed
Nach dem Login kopieren
Nach dem Login kopieren

2. Lösen Sie das Problem

Nach der Überprüfung des Codes wurde festgestellt, dass das Problem beim Aufruf von parseResponseStr() und der Verwendung von Response.body( .string erneut () als Parameter. Da ich es eilig hatte, habe ich online nachgeschaut und festgestellt, dass „response.body().string()“ nur einmal aufgerufen werden kann. Daher habe ich das Problem gelöst, indem ich die Logik in der Methode „onResponse()“ geändert habe:

getHttpClient().newCall(request).enqueue(new Callback() {
  @Override
  public void onFailure(Call call, IOException e) {}
  @Override
  public void onResponse(Call call, Response response) throws IOException {
    //此处,先将响应体保存到内存中
    String responseStr = response.body().string();
    if (BuildConfig.DEBUG) {
      Log.d(TAG, "onResponse: " + responseStr);
    }
    //解析请求体
    parseReponseStr(responseStr);
  }
});
Nach dem Login kopieren

3. Analysieren Sie das Problem anhand des Quellcodes

Nachdem das Problem gelöst ist, muss es anschließend noch analysiert werden. Da mein bisheriges Verständnis von OkHttp auf seine Verwendung beschränkt war und ich die Details seiner internen Implementierung nicht sorgfältig analysiert hatte, habe ich mir am Wochenende die Zeit genommen, einen Blick darauf zu werfen und die Ursache des Problems herauszufinden.

Lassen Sie uns zunächst die intuitivste Frage analysieren: Warum kann Response.body().string() nur einmal aufgerufen werden?

Um es auseinanderzunehmen, holen Sie sich zuerst das ResponseBody-Objekt (es ist eine abstrakte Klasse, wir müssen uns hier nicht um die spezifische Implementierungsklasse kümmern) über Response.body() und rufen Sie dann string() auf Methode von ResponseBody, um den Inhalt des Antworttexts abzurufen.

Nach der Analyse gibt es kein Problem mit der Methode body():

public final String string() throws IOException {
 return new String(bytes(), charset().name());
}
Nach dem Login kopieren

Es ist sehr einfach, den Zeichensatz (Charset) anzugeben Die byte()-Methode gibt das Byte[ ] zurück. Das Array wird in ein String-Objekt umgewandelt. Es gibt kein Problem mit der byte()-Methode:

public final byte[] bytes() throws IOException {
 //...
 BufferedSource source = source();
 byte[] bytes;
 try {
  bytes = source.readByteArray();
 } finally {
  Util.closeQuietly(source);
 }
 //...
 return bytes;
}
//... 表示删减了无关代码,下同。
Nach dem Login kopieren

In der byte()-Methode. Lesen Sie das Byte[]-Array über das BufferedSource-Schnittstellenobjekt und geben Sie es zurück. In Kombination mit der oben erwähnten Ausnahme ist mir die Methode Util.closeQuietly() im Codeblock „finally“ aufgefallen. Verzeihung? Lautlos schließen? ? ?

Diese Methode sieht seltsam aus? Schauen Sie nach:

public static void closeQuietly(Closeable closeable) {
 if (closeable != null) {
  try {
   closeable.close();
  } catch (RuntimeException rethrown) {
   throw rethrown;
  } catch (Exception ignored) {
  }
 }
}
Nach dem Login kopieren

Es stellt sich heraus, dass die oben erwähnte BufferedSource-Schnittstelle gemäß der Codedokumentation als Ressourcenpuffer verstanden werden kann Kommentare Implementierte die Closeable-Schnittstelle und schloss und gab Ressourcen frei, indem die Methode close() überschrieben wurde. Schauen Sie dann nach unten, um zu sehen, was die Methode close() bewirkt (im aktuellen Szenario ist die BufferedSource-Implementierungsklasse RealBufferedSource):

//持有的 Source 对象
public final Source source;
@Override
public void close() throws IOException {
 if (closed) return;
 closed = true;
 source.close();
 buffer.clear();
}
Nach dem Login kopieren

Offensichtlich wird die Ressource durch source.close() geschlossen und freigegeben. Apropos: Die Funktion der Methode closeQuietly () ist offensichtlich, nämlich das von der ResponseBody-Unterklasse gehaltene BufferedSource-Schnittstellenobjekt zu schließen.

Nachdem wir dies analysiert haben, stellen wir plötzlich fest: Wenn wir Response.body().string() zum ersten Mal aufrufen, gibt OkHttp die Pufferressourcen des Antworttextes zurück und ruft die Methode closeQuietly() auf, um sie stillschweigend freizugeben die Ressourcen.

Auf diese Weise kehren wir beim erneuten Aufrufen der string()-Methode immer noch zur obigen byte()-Methode zurück. Diesmal liegt das Problem in der Codezeile bytes = source.readByteArray(). Werfen wir einen Blick auf die readByteArray()-Methode von RealBufferedSource:

@Override
public byte[] readByteArray() throws IOException {
 buffer.writeAll(source);
 return buffer.readByteArray();
}
Nach dem Login kopieren

Schauen Sie sich weiterhin die writeAll()-Methode an:

@Override
public long writeAll(Source source) throws IOException {
  //...
  long totalBytesRead = 0;
  for (long readCount; (readCount = source.read(this, Segment.SIZE)) != -1; ) {
   totalBytesRead += readCount;
  }
  return totalBytesRead;
}
Nach dem Login kopieren

Das Problem liegt in der source.read() von for Schleife. Denken Sie daran, dass bei der Analyse der Methode close() oben source.close() aufgerufen wurde, um die Ressource zu schließen und freizugeben. Was passiert also, wenn die read()-Methode erneut aufgerufen wird:

@Override
public long read(Buffer sink, long byteCount) throws IOException {
  //...
  if (closed) throw new IllegalStateException("closed");
  //...
  return buffer.read(sink, toRead);
}
Nach dem Login kopieren

An diesem Punkt entspricht es dem Absturz, den ich zuvor erlebt habe:

java.lang.IllegalStateException: closed
Nach dem Login kopieren
Nach dem Login kopieren

4.OkHttp Warum ist es so konzipiert?

Durch die Manipulation des Quellcodes haben wir die Ursache des Problems gefunden, aber ich habe immer noch eine Frage: Warum ist OkHttp auf diese Weise konzipiert?

Tatsächlich lässt sich dieses Problem am besten verstehen, wenn man sich die Anmerkungsdokumentation von ResponseBody ansieht, da JakeWharton in den Ausgaben geantwortet hat:

reply of JakeWharton in okhttp issues
Nach dem Login kopieren

In einem einfachen Satz: Es ist auf ResponseBody dokumentiert lief, um das Klassenanmerkungsdokument zu lesen, und fasste es schließlich wie folgt zusammen:

在实际开发中,响应主体 RessponseBody 持有的资源可能会很大,所以 OkHttp 并不会将其直接保存到内存中,只是持有数据流连接。只有当我们需要时,才会从服务器获取数据并返回。同时,考虑到应用重复读取数据的可能性很小,所以将其设计为 一次性流(one-shot) ,读取后即 '关闭并释放资源'。

5.总结

最后,总结以下几点注意事项,划重点了:

1.响应体只能被使用一次;

2.响应体必须关闭:值得注意的是,在下载文件等场景下,当你以 response.body().byteStream() 形式获取输入流时,务必通过 Response.close() 来手动关闭响应体。

3.获取响应体数据的方法:使用 bytes() 或 string() 将整个响应读入内存;或者使用 source() , byteStream() , charStream() 方法以流的形式传输数据。

4.以下方法会触发关闭响应体:

Response.close()
Response.body().close()
Response.body().source().close()
Response.body().charStream().close()
Response.body().byteString().close()
Response.body().bytes()
Response.body().string()
Nach dem Login kopieren

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在Javascript中如何实现网页抢红包

详细解读ES6语法中可迭代协议

详细解读在React组件“外”如何使用父组件

微信小程序如何实现涂鸦

Das obige ist der detaillierte Inhalt vonWarum kann „response.body().string()' nicht mehrmals aufgerufen werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Konvertieren Sie grundlegende Datentypen mit der Java-Funktion String.valueOf() in Strings Konvertieren Sie grundlegende Datentypen mit der Java-Funktion String.valueOf() in Strings Jul 24, 2023 pm 07:55 PM

Konvertieren Sie grundlegende Datentypen mit der Java-Funktion String.valueOf() in Strings. Wenn wir in der Java-Entwicklung grundlegende Datentypen in Strings konvertieren müssen, ist eine gängige Methode die Verwendung der Funktion valueOf() der String-Klasse. Diese Funktion kann Parameter grundlegender Datentypen akzeptieren und die entsprechende Zeichenfolgendarstellung zurückgeben. In diesem Artikel untersuchen wir, wie die Funktion String.valueOf() für grundlegende Datentypkonvertierungen verwendet wird, und stellen einige Codebeispiele dafür bereit

So konvertieren Sie ein Char-Array in einen String So konvertieren Sie ein Char-Array in einen String Jun 09, 2023 am 10:04 AM

Methode zum Konvertieren eines char-Arrays in einen String: Dies kann durch Zuweisung erreicht werden. Verwenden Sie die Syntax {char a[]=" abc d\0efg ";string s=a;}, damit das char-Array dem String direkt einen Wert zuweisen und ausführen kann Der Code zum Abschließen der Konvertierung.

Verwenden Sie die String.replace()-Funktion von Java, um Zeichen (Strings) in einem String zu ersetzen Verwenden Sie die String.replace()-Funktion von Java, um Zeichen (Strings) in einem String zu ersetzen Jul 25, 2023 pm 05:16 PM

Ersetzen Sie Zeichen (Strings) in einem String mit der Java-Funktion String.replace(). In Java sind Strings unveränderliche Objekte, was bedeutet, dass der Wert eines String-Objekts nach der Erstellung nicht mehr geändert werden kann. Es kann jedoch vorkommen, dass Sie bestimmte Zeichen oder Zeichenfolgen in einer Zeichenfolge ersetzen müssen. Zu diesem Zeitpunkt können wir die Methode replace () in der String-Klasse von Java verwenden, um das Ersetzen von Zeichenfolgen zu implementieren. Die Methode replace() der Klasse String hat zwei Typen:

2w Wörter detaillierte Erklärung String, yyds 2w Wörter detaillierte Erklärung String, yyds Aug 24, 2023 pm 03:56 PM

Hallo zusammen, heute werde ich die Grundkenntnisse von Java: String mit Ihnen teilen. Unnötig zu erwähnen, wie wichtig die String-Klasse ist. Man kann sagen, dass sie die am häufigsten verwendete Klasse in unserer Back-End-Entwicklung ist, daher ist es notwendig, darüber zu sprechen.

Verwenden Sie die String.length()-Funktion von Java, um die Länge einer Zeichenfolge zu ermitteln Verwenden Sie die String.length()-Funktion von Java, um die Länge einer Zeichenfolge zu ermitteln Jul 25, 2023 am 09:09 AM

Verwenden Sie die String.length()-Funktion von Java, um die Länge einer Zeichenfolge zu ermitteln. In der Java-Programmierung ist die Zeichenfolge ein sehr häufiger Datentyp. Wir müssen häufig die Länge einer Zeichenfolge ermitteln, dh die Anzahl der Zeichen in der Zeichenfolge. In Java können wir die Funktion length() der String-Klasse verwenden, um die Länge eines Strings zu ermitteln. Hier ist ein einfacher Beispielcode: publicclassStringLengthExample{publ

So verwenden Sie die String-Klasse von Java So verwenden Sie die String-Klasse von Java Apr 19, 2023 pm 01:19 PM

1. String1 verstehen. Schauen wir uns zunächst den Quellcode der String-Klasse im JDK an. Sie können sehen, dass die String-Klasse nicht geändert werden kann Es gibt keine Unterklasse der String-Klasse, sodass alle Personen, die das JDK verwenden, dieselbe String-Klasse verwenden können Die Verwendung derselben Methode durch zwei verschiedene Personen führt zu unterschiedlichen Ergebnissen, was die Entwicklung des Codes unmöglich macht. Vererbung und Methodenüberschreibung bringen nicht nur Flexibilität, sondern führen auch dazu, dass sich viele Unterklassen unterschiedlich verhalten.

Fähigkeiten zur Byte-, Runen- und String-Konvertierung von Golang-Funktionen Fähigkeiten zur Byte-, Runen- und String-Konvertierung von Golang-Funktionen May 17, 2023 am 08:21 AM

In der Golang-Programmierung sind Byte-, Runen- und String-Typen sehr grundlegende und gängige Datentypen. Sie spielen eine wichtige Rolle bei der Verarbeitung von Datenoperationen wie Zeichenfolgen und Dateiströmen. Wenn wir diese Datenoperationen durchführen, müssen wir sie normalerweise ineinander konvertieren, was die Beherrschung einiger Konvertierungsfähigkeiten erfordert. In diesem Artikel werden die Konvertierungstechniken für Byte-, Runen- und String-Typen von Golang-Funktionen vorgestellt. Ziel ist es, den Lesern zu helfen, diese Datentypen besser zu verstehen und sie geschickt in der Programmierpraxis anwenden zu können.

So verwenden Sie die Split-Methode in Java String So verwenden Sie die Split-Methode in Java String May 02, 2023 am 09:37 AM

Die Split-Methode in String verwendet die Split()-Methode von String, um den String entsprechend den eingehenden Zeichen oder Strings aufzuteilen und das geteilte Array zurückzugeben. 1. Allgemeine Verwendung Bei Verwendung allgemeiner Zeichen wie @ oder als Trennzeichen: Stringaddress="Shanghai@Shanghai City@Minhang District@Wuzhong Road";String[]splitAddr=address.split("@");System .out. println(splitAddr[0]+splitAddr[1]+splitAddr[2]+splitAddr[3

See all articles