このページでは、
ネイティブ ブリッジ(JavaScript ブリッジとも呼ばれます)を確立して、
ウェブ コンテンツとホストの Android アプリケーション間の通信を容易にするためのさまざまな方法とベスト プラクティスについて説明します。WebView
これにより、ウェブ デベロッパーは JavaScript を使用して、標準のウェブ API では通常提供されないネイティブ プラットフォームの機能(カメラ、ファイル システム、高度なハードウェア センサーなど)にアクセスできます。
ユースケース
JavaScript ブリッジを実装すると、ウェブ コンテンツが Android オペレーティング システムへのより深いアクセスを必要とするさまざまな統合シナリオが可能になります。以下に、いくつかの例を示します。
- プラットフォームの統合: ウェブページからネイティブの Android UI コンポーネント(
生体認証プロンプト、
BottomSheetDialogなど)をトリガーします。 - パフォーマンス: 計算負荷の高いタスクをネイティブの Java または Kotlin コードにオフロードします。
- データの永続性: ローカルの暗号化されたデータベースまたは共有 設定にアクセスします。
- 大量のデータ転送: メディア ファイルや複雑なデータ構造を アプリとウェブ レンダラの間で渡します。
通信メカニズム
Android には、ネイティブ ブリッジを確立するための 3 つの主要な世代の API が用意されています。 これらはすべて現在も利用可能ですが、セキュリティ、使いやすさ、パフォーマンスが大きく異なります。
addWebMessageListener を使用する(推奨)
addWebMessageListener は、ウェブ コンテンツとネイティブ アプリコード間の通信に推奨される最新の方法です。JavaScript インターフェースの使いやすさとメッセージング システムのセキュリティを兼ね備えています。
仕組み: アプリは、特定の名前と一連の
許可されたオリジン ルールを持つリスナーを追加します。WebView は、ページの読み込みが開始された時点から、JavaScript オブジェクトがグローバル スコープ(window.objectName)に存在するようにします。
初期化: スクリプトが実行される前に WebView が JavaScript オブジェクトを挿入するようにするには、ページに移動する前に addWebMessageListener を呼び出す必要があります(WebViewCompat.navigate や loadUrl を呼び出すなど)。
主な機能:
セキュリティと信頼: 従来の API とは異なり、このメソッドでは初期化時に
Set<String>のallowedOriginRulesが必要です。これは、信頼を確立するための主要なメカニズムです。https://example.comなどの信頼できるオリジンを指定すると、WebView は、挿入された JavaScript オブジェクトを、そのオリジンから読み込まれたウェブページにのみ公開することを保証します。ネイティブ リスナー コールバックは、すべてのメッセージとともに
sourceOriginパラメータを受け取ります。ブリッジが複数の許可されたオリジンをサポートしている場合は、これを使用して送信元の正確なオリジンを確認できます。WebView は、プラットフォーム レベルでこれらのオリジン チェックを厳密に適用するため、アプリは通常、信頼できる
sourceOriginから受信したメッセージを真実として信頼できます。これにより、ほとんどの標準実装で厳格なペイロード検証を行う必要がなくなります。- WebView は、スキーム(HTTP/HTTPS)、ホスト、ポートに対してルールを照合します。
- WebView はパスを無視します。たとえば、
https://example.comではhttps://example.com/loginとhttps://example.com/homeが許可されます。 - WebView は、サブドメインのホストの先頭にワイルドカードを厳密に制限します。たとえば、
https://*.example.comはhttps://foo.example.comと一致しますが、https://example.comとは一致しません。https://example.comとそのサブドメインの両方を一致させる必要がある場合は、各 オリジン ルールを許可リストに個別に追加する必要があります(例:"https://example.com", "https://*.example.com")。スキームまたはドメインの中間にはワイルドカードを使用できません。
これにより、ブリッジは検証済みのドメインに制限され、許可されていない第三者のコンテンツや挿入された iframe がネイティブ コードを実行するのを防ぐことができます。
マルチフレームのサポート: オリジン ルールに一致するすべてのフレームで動作します。
スレッド: リスナー コールバックは、アプリケーションのメイン(UI) スレッドで実行されます。ブリッジで複雑なデータ処理、JSON 解析、データベース検索を行う必要がある場合は、その処理をバックグラウンド スレッドにオフロードして、「アプリが応答していません」(ANR)エラーでアプリケーション UI がフリーズしないようにする必要があります。
双方向: ウェブページがメッセージを送信すると、アプリは
JavaScriptReplyProxyを受け取ります。これを使用して、その 特定のフレームにメッセージを返信できます。このreplyProxyオブジェクトを保持し、いつでも使用して、ページが送信する個々のメッセージに返信するだけでなく、任意の数のメッセージをページに送信できます。元のフレームが移動または破棄された場合、プロキシでpostMessage()を使用して送信されたメッセージは無視されます。アプリ側からの開始: ウェブページは常にアプリとの 通信チャネルを開始する必要がありますが、ネイティブ アプリは一方的に ウェブページにこのプロセスを開始するよう求めることができます。ネイティブ アプリは、 ウェブページと
addDocumentStartJavaScript()(ページの読み込み前に JavaScript を評価)またはevaluateJavaScript()(ページの読み込み後に JavaScript を評価)を使用して通信できます。
制限事項: この API は、データを文字列または byte[] 配列として送信します。JSON オブジェクトなどの複雑なデータ構造の場合は、これらの形式のいずれかにシリアル化し、反対側で逆シリアル化してデータ構造を再構築する必要があります。
使用例:
双方向メッセージ交換の完全なシーケンスを理解するには、イベントが次の順序で進行します。
- 開始(アプリ): ネイティブ アプリはリスナーを
addWebMessageListenerで登録し、ページ ナビゲーションを開始します(WebViewCompat.navigateやloadUrlなど)。 - メッセージ送信(ウェブ): ウェブページの JavaScript が
myObject.postMessage(message)を呼び出して通信を開始します。 - メッセージの受信と返信(アプリ): アプリは
リスナー コールバックでメッセージを受信し、提供された
replyProxy.postMessage()を使用して返信します。 - 返信の受信(ウェブ): ウェブページは、
myObject.onmessage()コールバック関数で非同期の返信を受け取ります。
Kotlin
val myListener = WebViewCompat.WebMessageListener { _, _, _, _, replyProxy -> // Handle the message from JS replyProxy.postMessage("Acknowledged!") } // Check whether the WebView version supports the feature. if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { val allowedOrigins = setOf("https://www.example.com") WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener) }
Java
WebMessageListener myListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Handle the message from JS
replyProxy.postMessage("Acknowledged!");
};
// Check whether the WebView version supports the feature.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
Set<String> allowedOrigins = Set.of("https://www.example.com");
WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener);
}
次の JavaScript は、addWebMessageListener のクライアントサイド実装を示しています。これにより、ウェブ コンテンツはネイティブ アプリからメッセージを受信し、myObject プロキシを介して独自のメッセージを送信できます。
myObject.onmessage = function(event) {
console.log("App says: " + event.data);
};
myObject.postMessage("Hello world!");
postWebMessage を使用する(代替)
Android は、ウェブの window.postMessage と同様の非同期メッセージング ベースの代替手段を提供するために、これを導入しました。
仕組み: アプリは WebViewCompat.postWebMessage を使用して、ペイロードを
ウェブページのメインフレームに送信します。双方向通信
チャネルを確立するには、WebMessageChannel を作成し、そのポートの 1 つをメッセージとともにウェブ コンテンツに渡します。
特徴:
- 非同期:
addWebMessageListenerと同様に、このメソッドは 非同期メッセージングを使用します。これにより、アプリがバックグラウンドでデータを処理している間も、ウェブページは ユーザー操作に応答し続けることができます。 - オリジン認識:
targetOriginを指定して、WebView が信頼できるウェブサイトにのみデータを配信するようにできます。
制限事項:
- スコープ: この API は、通信をメインフレームに制限します。iframe への直接アドレス指定やメッセージの送信はサポートされていません。
- URI の制限: ターゲット オリジンとして「*」を指定しない限り、
data:URI、file:URI、またはloadData()を使用して読み込まれたコンテンツにこのメソッドを使用することはできません。これにより、どのページでもメッセージを受信できます。 - ID のリスク: ウェブ コンテンツが 送信者の ID を確認する明確な方法はありません。ウェブページが受信したメッセージは、ネイティブ アプリまたは別の iframe から送信された可能性があります。
addWebMessageListener をサポートしていない以前の Android バージョンで、文字列ベースのデータ用のシンプルな非同期チャネルが必要な場合は、このメソッドを使用します。
addJavascriptInterface を使用する(レガシー)
最も古い方法は、ネイティブ オブジェクト インスタンスを WebView に直接挿入する方法です。
仕組み: Kotlin または Java クラスを定義し、許可された
メソッドに @JavascriptInterface アノテーションを付け、クラスのインスタンスを
WebView に addJavascriptInterface(Object, String) を使用して追加します。
特徴:
- 同期: Android コードの メソッドが戻るまで、JavaScript 実行環境がブロックされます。
- スレッド セーフティ: システムはバックグラウンド スレッドでメソッドを呼び出すため、 Kotlin または Java 側で慎重に同期する必要があります。
- セキュリティ リスク: デフォルトでは、
addJavascriptInterfaceは iframe を含む WebView 内の すべてのフレームで使用できます。オリジン ベースのアクセス制御はありません。WebView の非同期動作により、インターフェースを呼び出すフレームの URL を安全に特定することはできません。セキュリティ検証にWebView.getUrl()などのメソッドを使用しないでください。正確であることが保証されておらず、リクエストを行った特定のフレームを示すものではありません。
データ型の変換と強制型変換
addJavascriptInterface を使用する場合、Chromium ベースの Java ブリッジは、JavaScript ランタイムと Android アプリコードの間でデータ型を変換します。
メソッドのパラメータと戻り値には、次の強制型変換ルールが適用されます。
パラメータ型のマッピング(JavaScript から Java)
JavaScript がアノテーション付きの Java または Kotlin メソッドに引数を渡すと、ブリッジは JavaScript の値を対応する Java パラメータ型に強制的に変換します。
| Java パラメータ型 | JavaScript 引数値 | 強制型変換の動作 |
|---|---|---|
byte、short、int、long |
数値(整数) | 値はターゲットの整数型にキャストされます。範囲外の値は、標準の数値キャスト ルールに従って折り返されます。 |
byte、short、int、long |
NaN |
0 に強制的に変換されます。 |
byte、short、int、long |
Infinity |
byte と short の場合は -1、int と long の場合は Integer.MAX_VALUE と Long.MAX_VALUE に強制的に変換されます。 |
float、double |
数値 | 対応する Java 浮動小数点値に強制的に変換されます。 |
float、double |
NaN / Infinity |
Float.NaN、Double.NaN、Float.POSITIVE_INFINITY、Double.POSITIVE_INFINITY に強制的に変換されます。 |
char |
数値(整数) | 対応する Unicode コードポイントに変換されます。 |
char |
整数以外、NaN、Infinity |
\u0000 に強制的に変換されます。 |
boolean |
true / false |
Java の true または false に強制的に変換されます。 |
boolean |
数値、文字列、オブジェクト | false に強制的に変換されます(空でない文字列とゼロ以外の数値を含む)。 |
String |
文字列 | 文字列値は保持されます。 |
String |
数値、ブール値 | 文字列表現としてフォーマットされます("42"、"true"、"false" など)。 |
String |
null / undefined |
null は Java の null に強制的に変換されます。undefined はリテラル文字列 "undefined" に強制的に変換されます。 |
String |
オブジェクト、ArrayBuffer、TypedArray | リテラル文字列 "undefined" に強制的に変換されます。 |
プリミティブ配列(int[]、byte[]、boolean[] など)または String[] |
配列([...]) |
ターゲット要素型の 1 次元 Java 配列に変換されます。スパース配列では、割り当てられていないインデックスがデフォルト値(0、false、null)で埋められます。 |
プリミティブ配列(int[]、byte[] など) |
TypedArray(Int8Array、Uint8Array、Int32Array、Float64Array) |
要素は対応する Java プリミティブ配列に強制的に変換されます。 |
多次元配列(int[][] など) |
ネストされた配列([[...]]) |
サポート対象外 。多次元配列パラメータは null に評価されます。 |
ArrayBuffer、DataView |
ArrayBuffer、DataView |
配列としては対象外です。ArrayBuffer インスタンスと DataView インスタンスは null に評価されます。 |
Object またはカスタムクラス |
JavaScript オブジェクト({...}) |
サポート対象外 。任意の JavaScript オブジェクト リテラルは Java で null に評価されます。 |
Object またはカスタムクラス |
挿入された Java オブジェクト ラッパー | サポート対象(ラウンド トリップ)。基になる Java インスタンスを Java メソッドに渡します。Java 型がパラメータ シグネチャと一致しない場合は、JavaScript 例外をスローします。 |
ボックス化された型(Integer、Double、Boolean など) |
数値、ブール値 | サポート対象外 。ボックス化されたプリミティブ型は不透明なオブジェクトとして扱われ、null に評価されます。 |
| 任意のプリミティブ型 | null / undefined |
デフォルト値(0、0.0、\u0000、false)に強制的に変換されます。 |
Object、String、配列 |
null |
Java の null に強制的に変換されます。 |
戻り値の型のマッピング(Java から JavaScript)
アノテーション付きの Java または Kotlin メソッドが値を返すと、ブリッジはその値を JavaScript 型に変換します。
| Java 戻り値の型 | JavaScript 値 | JavaScript typeof |
|---|---|---|
boolean |
true / false |
"boolean" |
byte、short、int、long、float、double |
数値 | "number" |
char |
数値(Unicode コードポイント) | "number" |
String(null 以外) |
文字列値 | "string" |
String(null) |
undefined |
"undefined" |
void |
undefined |
"undefined" |
Java 配列(int[]、String[] など) |
undefined |
"undefined"。「配列の戻り値はサポートされていません。」Java メソッドは実行されず、例外を発生させることなく undefined が返されます。 |
| Java オブジェクト / カスタム型(null 以外) | オブジェクト ラッパー | "object"。「object」Java インスタンスの周囲に JavaScript ラッパーを作成します。JavaScript コードは、@JavascriptInterface でアノテーションが付けられたこのオブジェクトの任意の public メソッドを呼び出すことができます。 |
Java オブジェクト / カスタム型(null) |
null |
"object" |
ボックス化されたプリミティブ(Integer、Double など) |
オブジェクト ラッパー | "object"。アクセス可能な @JavascriptInterface メソッドがない不透明な Java オブジェクト ラッパーとして返されるため、JavaScript で値を使用できません。 |
メソッドとメンバーのアクセシビリティ
JavaScript ブリッジは、意図しないコード実行を防ぐために、厳格なメンバー アクセスと可視性ルールを適用します。
- フィールドは公開されない: Java フィールド(
publicフィールドとpublic finalフィールドを含む)は JavaScript からアクセスできず、undefinedに評価されます。 - アノテーションの要件:
@JavascriptInterfaceで明示的にアノテーションが付けられたメソッドのみが JavaScript に公開されます。 - 可視性の制限: メソッドは
publicである必要があります。privateメソッドとprotectedメソッドは、@JavascriptInterfaceアノテーションが付いていても、JavaScript に公開されることはありません。 - 静的メソッド:
@JavascriptInterfaceでアノテーションが付けられた静的メソッドは、JavaScript から 呼び出すことができます。 - 継承とオーバーライド:
@JavascriptInterfaceアノテーションは、サブクラスがメソッドをオーバーライドしても 継承されません。サブクラスがスーパークラスのアノテーション付きメソッドをオーバーライドする場合、サブクラスはオーバーライドされたメソッドに@JavascriptInterfaceアノテーションを明示的に含めて、JavaScript に公開する必要があります。スーパークラスから継承されたオーバーライドされていない public メソッドは、スーパークラスでアノテーションが付けられている場合は引き続きアクセスできます。 - リフレクション保護: 標準の Java リフレクション メソッド(
getClass()など)はブロックされ、リモート コード実行の脆弱性を防ぐために JavaScript 例外がスローされます。 - メソッドのオーバーロード: オーバーロードされた Java メソッドがサポートされています。ブリッジは、渡された引数の数のみに基づいてメソッド呼び出しを解決し、引数の型は考慮しません。引数の数が無効なオーバーロードされたメソッドを呼び出すと、JavaScript 例外が発生します。2 つのオーバーロードの引数の数が同じ場合、いずれかが任意に選択されます。
メカニズムの概要
次の表は、3 つの主要なネイティブ ブリッジ実装メカニズムを簡単に比較したものです。
| メソッド | addWebMessageListener |
postWebMessage |
addJavascriptInterface |
|---|---|---|---|
| 実装 | 非同期(メインスレッドのリスナー) | 非同期 | 同期 |
| セキュリティ | 最高(許可リスト ベース) | 高(オリジン認識) | 低(オリジン チェックなし) |
| 複雑さ | 中 | 中 | シンプル |
| 方向 | 双方向 | 双方向 | ウェブからアプリ |
| WebView の最小バージョン | バージョン 82(Jetpack Webkit 1.3.0) | バージョン 45(Jetpack Webkit 1.1.0) | すべてのバージョン |
| 推奨 | ○ | いいえ | いいえ |
大量のデータ転送を処理する
数メガバイトの文字列やバイナリ ファイルなどの大きなペイロードを転送する場合は、メモリを慎重に管理して、32 ビット デバイスでアプリケーション応答なし(ANR)エラーやクラッシュが発生しないようにする必要があります。このセクションでは、ホスト アプリケーションとウェブ コンテンツ間で大量のデータを転送する際のさまざまな手法と制限について説明します。
バイト配列でバイナリデータを転送する
WebMessageCompat クラスを使用すると、バイナリデータを Base64 文字列にシリアル化する代わりに、byte[] 配列を直接送信できます。Base64 はデータサイズに約 33% のオーバーヘッドを追加するため、これはメモリ効率が大幅に向上し、高速になります。
- バイナリの利点: ネイティブ アプリとウェブ コンテンツ間で、画像ファイルや音声などのバイナリデータを転送します。
- 制限事項: バイト配列を使用しても、システムはアプリと WebView がウェブ コンテンツのレンダリングに使用する分離プロセス間の プロセス間通信(IPC)境界を越えてデータをコピーします。非常に大きなファイルの場合、これでもかなりのメモリが消費されます。
次のコード例は、WebMessageCompat.TYPE_ARRAY_BUFFER でマークされたメッセージを受信し、WebViewFeature.MESSAGE_ARRAY_BUFFER をチェックしてバイナリデータで任意に返信するよう、ネイティブ アプリ側で addWebMessageListener を設定する方法を示しています。
Kotlin
fun setupWebView(webView: WebView) { if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { val listener = WebViewCompat.WebMessageListener { view, message, sourceOrigin, isMainFrame, replyProxy -> // Check if the received message is an ArrayBuffer if (message.type == WebMessageCompat.TYPE_ARRAY_BUFFER) { val binaryData: ByteArray = message.arrayBuffer // Process your binary data (image, audio, etc.) println("Received bytes: ${binaryData.size}") // Optional: Send a binary reply back to JavaScript. // This example sends a 3-byte array for simplicity. if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) { val replyBytes = byteArrayOf(0x01, 0x02, 0x03) replyProxy.postMessage(replyBytes) } } } // "myBridge" matches the window.myBridge in JavaScript WebViewCompat.addWebMessageListener( webView, "myBridge", setOf("https://example.com"), // Security: restrict origins listener ) } }
Java
public void setupWebView(WebView webView) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
WebViewCompat.WebMessageListener listener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Check if the received message is an ArrayBuffer
if (message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
byte[] binaryData = message.getArrayBuffer();
// Process your binary data (image, audio, etc.)
System.out.println("Received bytes: " + binaryData.length);
// Optional: Send a binary reply back to JavaScript.
// This example sends a 3-byte array for simplicity.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
byte[] replyBytes = new byte[]{0x01, 0x02, 0x03};
replyProxy.postMessage(replyBytes);
}
}
};
// "myBridge" matches the window.myBridge in JavaScript
WebViewCompat.addWebMessageListener(
webView,
"myBridge",
Set.of("https://example.com"), // Security: restrict origins
listener
);
}
}
次の JavaScript コードは、addWebMessageListener のクライアントサイド実装を示しています。これにより、ウェブ コンテンツは、前の例で挿入された window.myBridge プロキシを使用して、ネイティブ アプリとの間でバイナリデータ(ArrayBuffer)を送受信できます。
// Function to send an image or binary buffer to the app
async function sendBinaryToApp() {
const response = await fetch('image.jpg');
const buffer = await response.arrayBuffer();
// Check if the injected bridge object exists
if (window.myBridge) {
// You can send the ArrayBuffer directly
window.myBridge.postMessage(buffer);
}
}
// Receiving binary data from the app
if (window.myBridge) {
window.myBridge.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) {
console.log('Received binary data from App, length:', event.data.byteLength);
// Process the binary data (for example, as a Uint8Array)
const bytes = new Uint8Array(event.data);
console.log('First byte:', bytes[0]);
}
};
}
大規模なデータを効率的に読み込む
非常に大きなファイル(10 MB 超)の場合は、shouldInterceptRequest メソッドを使用して
データをストリーミングします。
- ウェブページは、カスタムのプレースホルダ URL に対して
fetch()呼び出しを開始します。例:https://app.local/large-file。 - Android アプリは、
WebViewClient.shouldInterceptRequest でこのリクエストをインターセプトします。 - アプリはデータを
InputStreamとして返します。
これにより、ペイロード全体を一度にメモリに読み込むのではなく、データをチャンク単位でストリーミングできます。
次の JavaScript 関数は、カスタムのプレースホルダ URL への標準の fetch() 呼び出しを使用して、ネイティブ アプリケーションから大きなバイナリ ファイルを効率的に読み込むためのクライアントサイド コードを示しています。
async function fetchBinaryFromApp() {
try {
// This URL doesn't need to exist on the internet
const response = await fetch('https://app.local/data/large-file.bin');
if (!response.ok) throw new Error('Network response was not okay');
// For raw binary data:
const arrayBuffer = await response.arrayBuffer();
console.log('Received binary data, size:', arrayBuffer.byteLength);
// Process buffer (for example, new Uint8Array(arrayBuffer))
/*
// OR for an image:
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
document.getElementById('myImage').src = imageUrl;
*/
} catch (error) {
console.error('Fetch error:', error);
}
}
次のコード例は、ネイティブ アプリ側で Kotlin と Java の両方で WebViewClient.shouldInterceptRequest メソッドを使用して、ウェブ コンテンツによってリクエストされたカスタムのプレースホルダ URL をインターセプトして、大きなバイナリ ファイルをストリーミングする方法を示しています。
Kotlin
webView.webViewClient = object : WebViewClient() { override fun shouldInterceptRequest( view: WebView?, request: WebResourceRequest? ): WebResourceResponse? { val url = request?.url ?: return null // Check if this is our custom placeholder URL if (url.host == "app.local" && url.path == "/data/large-file.bin") { try { // 1. Get your data as an InputStream // (from Assets, Files, or a generated byte stream) val inputStream: InputStream = context.assets.open("my_data.pb") // 2. Define Response Headers (Crucial for CORS/Fetch) val headers = mutableMapOf<String, String>() headers["Access-Control-Allow-Origin"] = "*" // Allow fetch from any origin // 3. Return the response return WebResourceResponse( "application/octet-stream", // MIME type (for example, image/jpeg) "UTF-8", // Encoding 200, // Status Code "OK", // Reason Phrase headers, // Custom Headers inputStream // The actual data stream ) } catch (e: Exception) { // Handle exception } } return super.shouldInterceptRequest(view, request) } }
Java
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String urlPath = request.getUrl().getPath();
String host = request.getUrl().getHost();
// Check if this is our custom placeholder URL
if ("app.local".equals(host) && "/data/large-file.bin".equals(urlPath)) {
try {
// 1. Get your data as an InputStream
// (from Assets, Files, or a generated byte stream)
InputStream inputStream = getContext().getAssets().open("my_data.pb");
// 2. Define Response Headers (Crucial for CORS/Fetch)
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Origin", "*"); // Allow fetch from any origin
// 3. Return the response
return new WebResourceResponse(
"application/octet-stream", // MIME type (for example, image/jpeg)
"UTF-8", // Encoding
200, // Status Code
"OK", // Reason Phrase
headers, // Custom Headers
inputStream // The actual data stream
);
} catch (Exception e) {
// Handle exception
}
}
return super.shouldInterceptRequest(view, request);
}
});
セキュリティに関する推奨事項に従う
アプリケーションとユーザーデータを保護するため、ブリッジを実装する際は次のガイドラインに従ってください。
HTTPS を適用する: 悪意のある第三者のコンテンツがアプリケーションのネイティブ ロジックを 呼び出せないように、安全な オリジンとの通信のみを許可します。
オリジン ルールに依存する: 信頼を処理する最善の方法は、 厳密に
allowedOriginRulesを定義し、メッセージ コールバックで提供されるsourceOriginを確認することです。絶対に必要な場合を除き、すべてのオリジンに一致する完全なワイルドカード(*)を唯一のオリジン ルールとして使用しないでください。サブドメインにワイルドカード(*.example.comなど)を使用しても、複数のサブドメイン(foo.example.com、bar.example.comなど)に一致させる場合は有効で安全です。注: オリジン ルールは、悪意のあるサードパーティのウェブサイト や非表示の iframe から保護しますが、信頼できるドメイン内のクロスサイト スクリプティング(XSS) の脆弱性から保護することはできません。たとえば、ウェブページにユーザー作成コンテンツが表示され、保存された XSS に対して脆弱性がある場合、攻撃者は信頼できるオリジンとして動作するスクリプトを実行する可能性があります。機密性の高いネイティブ プラットフォーム オペレーションを実行する前に、メッセージ ペイロードに検証を適用することを検討してください。
サーフェス領域を最小限に抑える: ウェブページに必要な特定のメソッドまたはデータのみを公開します。
実行時に機能を確認する:
addWebMessageListenerなどの最新のブリッジ API は、Jetpack Webkit ライブラリの一部です。そのため、呼び出す前にWebViewFeature.isFeatureSupported()を使用してサポートを確認してください。