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()); } });
java.lang.IllegalStateException: closed
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); } });
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()); }
public final byte[] bytes() throws IOException { //... BufferedSource source = source(); byte[] bytes; try { bytes = source.readByteArray(); } finally { Util.closeQuietly(source); } //... return bytes; } //... 表示删减了无关代码,下同。
public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } } }
//持有的 Source 对象 public final Source source; @Override public void close() throws IOException { if (closed) return; closed = true; source.close(); buffer.clear(); }
@Override public byte[] readByteArray() throws IOException { buffer.writeAll(source); return buffer.readByteArray(); }
@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; }
@Override public long read(Buffer sink, long byteCount) throws IOException { //... if (closed) throw new IllegalStateException("closed"); //... return buffer.read(sink, toRead); }
java.lang.IllegalStateException: closed
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
在实际开发中,响应主体 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()
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
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!