検索
ホームページウェブフロントエンドjsチュートリアルなぜresponse.body().string()を複数回呼び出すことができないのでしょうか?

最近 Okhttp を使用していたときに、落とし穴を踏んだことがあると思います。将来同じような問題が発生したときに回避できるように、ここで共有します。この記事は、ソース コードの観点から問題の根本を分析することに焦点を当てており、有益な情報が満載です。

1. 問題が見つかりました 開発中に、OkHttpClient オブジェクトを構築してリクエストを開始し、サーバーが応答した後、コールバック インターフェイスが onResponse() メソッドをトリガーして渡しました。このメソッドの Response オブジェクト処理は結果を返し、ビジネス ロジックを実装します。コードはおおよそ次のとおりです。

//注:为聚焦问题,删除了无关代码
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());
  }
});

onResponse() では、デバッグの便宜のために、戻りの本文を出力し、parseResponseStr() メソッドを通じて戻りの本文を解析しました (注: response.body().string( ) はここで 2 回呼び出されます)。

このコードは問題がないように見えましたが、実際には実行後に問題が発生しました。コンソールを通して、戻り本体データ (json) が正常に出力されたことがわかりましたが、例外がスローされました:

java.lang.IllegalStateException: closed

2。問題 コードを確認したところ、parseResponseStr() を呼び出すときに、response.body().string() がパラメータとして再度使用されていることが問題であることがわかりました。急いでいたのでオンラインで調べたところ、response.body().string() は 1 回しか呼び出せないことがわかったので、onResponse() メソッドのロジックを変更して問題を解決しました。 . ソースコードで問題を分析します

問題は解決しましたが、その後もまだ分析する必要があります。私はこれまで OkHttp についてその用途のみを理解していて、内部実装の詳細を注意深く分析していなかったので、週末に時間をかけて OkHttp を観察し、問題の原因を突き止めました。 最初に最も直感的な質問を分析してみましょう: なぜ response.body().string() は 1 回しか呼び出せないのでしょうか? 逆アセンブリを見ると、まず、response.body() を通じて ResponseBody オブジェクト (抽象クラスです。ここでは特定の実装クラスを気にする必要はありません) を取得し、次に ResponseBody の string() メソッドを呼び出します。応答本文の内容を取得します。

分析後、body() メソッドには問題はありません。string() メソッドを見てみましょう:

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

これは、byte() メソッドによって返された byte[] 配列を String オブジェクトに変換します。文字セット (charset) を指定し、構築します。問題ありません。引き続き byte() メソッドを見てください:

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

byte() メソッドでは、BufferedSource インターフェイス オブジェクトを通じて byte[] 配列を読み取り、それを返します。前述の例外と組み合わせると、finally コード ブロックに Util.closeQuietly() メソッドがあることに気付きました。すみません?静かに閉じますか? ? ?

このメソッドは奇妙に見えますね? close() メソッドをコピーして、リソースを閉じて解放するインターフェース。次に、close() メソッドが何を行うかを確認します (現在のシナリオでは、BufferedSource 実装クラスは RealBufferedSource です):

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

明らかに、リソースはsource.close() を通じて閉じられ、解放されます。そういえば、closeQuietly() メソッドの機能は自明であり、ResponseBody サブクラスが保持する BufferedSource インターフェイス オブジェクトを閉じることです。

分析のこの時点で、私たちは突然気づきました。response.body().string() を初めて呼び出すと、OkHttp は応答本文のバッファー リソースを返し、closeQuietly() メソッドを呼び出してサイレントにリソース。

このように、string() メソッドを再度呼び出すと、やはり上記の byte() メソッドに戻ります。今回の問題は、コードの bytes = source.readByteArray() 行にあります。 RealBufferedSource の readByteArray() メソッドを見てみましょう:

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

引き続き writeAll() メソッドを見てみましょう:

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

問題は for ループの source.read() にあります。上記の close() メソッドを分析するときに、source.close() を呼び出してリソースを閉じて解放したことを思い出してください。では、read() メソッドが再度呼び出されるとどうなるでしょうか:

@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;
}

4. OkHttp はなぜこのように設計されているのでしょうか。

ソース コードを修正することで、問題の根本が見つかりました。しかし、まだ疑問が残っています。なぜ OkHttp はこのように設計されているのでしょうか?

実際、この問題を理解する最良の方法は、JakeWharton が問題で答えたように、ResponseBody のアノテーション ドキュメントを参照することです。

@Override
public long read(Buffer sink, long byteCount) throws IOException {
  //...
  if (closed) throw new IllegalStateException("closed");
  //...
  return buffer.read(sink, toRead);
}
簡単な文で言うと、次のようになります。 、最終的な要約は次のとおりです:

在实际开发中,响应主体 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()

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

相关文章:

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

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

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

微信小程序如何实现涂鸦

以上がなぜresponse.body().string()を複数回呼び出すことができないのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JavaScriptはCで書かれていますか?証拠を調べるJavaScriptはCで書かれていますか?証拠を調べるApr 25, 2025 am 12:15 AM

はい、JavaScriptのエンジンコアはCで記述されています。1)C言語は、JavaScriptエンジンの開発に適した効率的なパフォーマンスと基礎となる制御を提供します。 2)V8エンジンを例にとると、そのコアはCで記述され、Cの効率とオブジェクト指向の特性を組み合わせて書かれています。3)JavaScriptエンジンの作業原理には、解析、コンパイル、実行が含まれ、C言語はこれらのプロセスで重要な役割を果たします。

JavaScriptの役割:WebをインタラクティブでダイナミックにするJavaScriptの役割:WebをインタラクティブでダイナミックにするApr 24, 2025 am 12:12 AM

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。

CおよびJavaScript:接続が説明しましたCおよびJavaScript:接続が説明しましたApr 23, 2025 am 12:07 AM

CおよびJavaScriptは、WebAssemblyを介して相互運用性を実現します。 1)CコードはWebAssemblyモジュールにコンパイルされ、JavaScript環境に導入され、コンピューティングパワーが強化されます。 2)ゲーム開発では、Cは物理エンジンとグラフィックスレンダリングを処理し、JavaScriptはゲームロジックとユーザーインターフェイスを担当します。

Webサイトからアプリまで:JavaScriptの多様なアプリケーションWebサイトからアプリまで:JavaScriptの多様なアプリケーションApr 22, 2025 am 12:02 AM

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

Python vs. JavaScript:ユースケースとアプリケーションと比較されますPython vs. JavaScript:ユースケースとアプリケーションと比較されますApr 21, 2025 am 12:01 AM

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。

JavaScript通訳者とコンパイラにおけるC/Cの役割JavaScript通訳者とコンパイラにおけるC/Cの役割Apr 20, 2025 am 12:01 AM

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

JavaScript in Action:実際の例とプロジェクトJavaScript in Action:実際の例とプロジェクトApr 19, 2025 am 12:13 AM

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

JavaScriptとWeb:コア機能とユースケースJavaScriptとWeb:コア機能とユースケースApr 18, 2025 am 12:19 AM

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール