หน้านี้จะกล่าวถึงวิธีการต่างๆ และแนวทางปฏิบัติแนะนำในการสร้าง
บริดจ์เนทีฟ หรือที่เรียกว่าบริดจ์ JavaScript เพื่ออำนวยความสะดวกในการสื่อสาร
ระหว่างเนื้อหาเว็บใน WebView กับแอปพลิเคชัน Android โฮสต์
ซึ่งช่วยให้นักพัฒนาเว็บใช้ JavaScript เพื่อเข้าถึงฟีเจอร์ของแพลตฟอร์มเนทีฟ เช่น กล้อง ระบบไฟล์ หรือเซ็นเซอร์ฮาร์ดแวร์ขั้นสูง ซึ่งโดยปกติแล้ว Web API มาตรฐานจะไม่มีให้
กรณีการใช้งาน
การติดตั้งใช้งานบริดจ์ JavaScript ช่วยให้เกิดสถานการณ์การผสานรวมต่างๆ ที่เนื้อหาเว็บต้องเข้าถึงระบบปฏิบัติการ Android ในระดับที่ลึกขึ้น ตัวอย่างมีดังนี้
- การผสานรวมแพลตฟอร์ม: การเรียกใช้คอมโพเนนต์ UI ของ Android แบบเนทีฟ (เช่น
ข้อความแจ้งไบโอเมตริก
BottomSheetDialog) จากหน้าเว็บ - ประสิทธิภาพ: การส่งต่อภาระงานด้านการคำนวณที่หนักหน่วงไปยังโค้ด Java หรือ Kotlin แบบเนทีฟ
- การคงอยู่ของข้อมูล: การเข้าถึงฐานข้อมูลที่เข้ารหัสในเครื่องหรือค่ากำหนดที่แชร์
- การโอนข้อมูลขนาดใหญ่: การส่งไฟล์สื่อหรือโครงสร้างข้อมูลที่ซับซ้อน ระหว่างแอปกับเครื่องมือแสดงผลบนเว็บ
กลไกการสื่อสาร
Android มี API หลัก 3 รุ่นเพื่อสร้างบริดจ์เนทีฟ แม้ว่าฟีเจอร์เหล่านี้จะยังคงใช้งานได้ แต่ก็มีความแตกต่างกันอย่างมากในด้านความปลอดภัย ความสามารถในการใช้งาน และประสิทธิภาพ
ใช้ addWebMessageListener (แนะนำ)
addWebMessageListener เป็นแนวทางที่ทันสมัยที่สุดและขอแนะนำสำหรับการ
สื่อสารระหว่างเนื้อหาเว็บกับโค้ดแอปเนทีฟ โดยผสานความสะดวก
ในการใช้อินเทอร์เฟซ JavaScript เข้ากับความปลอดภัยของระบบการรับส่งข้อความ
วิธีการทำงาน: แอปจะเพิ่ม Listener ที่มีชื่อเฉพาะและชุด
กฎของแหล่งที่มาที่อนุญาต จากนั้น WebView จะตรวจสอบว่าออบเจ็กต์ JavaScript อยู่ในขอบเขตส่วนกลาง (window.objectName) ตั้งแต่หน้าเว็บเริ่มโหลด
การเริ่มต้น: หากต้องการให้ WebView แทรกออบเจ็กต์ JavaScript ก่อนที่สคริปต์จะทำงาน คุณต้องเรียกใช้ addWebMessageListener ก่อนเรียกใช้ loadUrl()
ฟีเจอร์หลัก
ความปลอดภัยและความน่าเชื่อถือ: เมธอดนี้ต้องใช้
Set<String>ของallowedOriginRulesในระหว่างการเริ่มต้น ซึ่งแตกต่างจาก API เดิม ซึ่งเป็น กลไกหลักในการสร้างความน่าเชื่อถือเมื่อคุณระบุต้นทางที่เชื่อถือได้ เช่น
https://example.comWebView จะรับประกันว่าระบบจะแสดงออบเจ็กต์ JavaScript ที่แทรกไปยัง หน้าเว็บที่โหลดจากต้นทางนั้นเท่านั้นการเรียกกลับของ Listener เนทีฟจะได้รับพารามิเตอร์
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 ที่แทรกทำงานโค้ดดั้งเดิม
รองรับหลายเฟรม: ทำงานในทุกเฟรมที่ตรงกับกฎต้นทาง
การแยกเธรด: การเรียกกลับของ Listener จะทำงานในเธรดหลัก (UI) ของแอปพลิเคชัน หากบริดจ์ต้องจัดการการประมวลผลข้อมูลที่ซับซ้อน การแยกวิเคราะห์ JSON หรือการค้นหาฐานข้อมูล คุณต้องส่งต่อการทำงานดังกล่าวไปยังเธรดเบื้องหลังเพื่อป้องกันไม่ให้ UI ของแอปพลิเคชันหยุดทำงานเนื่องจากข้อผิดพลาด "แอปไม่ตอบสนอง" (ANR)
สองทิศทาง: เมื่อหน้าเว็บส่งข้อความ แอปจะได้รับ
JavaScriptReplyProxyที่ใช้ส่งข้อความกลับไปยังเฟรมนั้นๆ คุณสามารถเก็บออบเจ็กต์replyProxyนี้ไว้และใช้ได้ทุกเมื่อเพื่อส่งข้อความจำนวนเท่าใดก็ได้ไปยังเพจ ไม่ใช่แค่เพื่อตอบกลับข้อความแต่ละรายการที่เพจส่งมา หากเฟรมต้นทางไปยังที่อื่น หรือถูกทำลาย ระบบจะเพิกเฉยต่อข้อความที่ส่งโดยใช้postMessage()ในพร็อกซีโดยไม่มีการแจ้งเตือนการเริ่มต้นฝั่งแอป: แม้ว่าหน้าเว็บจะต้องเริ่มต้นช่องทางการสื่อสารกับแอปเสมอ แต่แอปเนทีฟก็สามารถแจ้งให้หน้าเว็บเริ่มกระบวนการนี้ได้โดยฝ่ายเดียว แอปเนทีฟสามารถสื่อสารกับหน้าเว็บได้โดยใช้
addDocumentStartJavaScript()(เพื่อประเมิน JavaScript ก่อนที่หน้าเว็บจะโหลด) หรือevaluateJavaScript()(เพื่อประเมิน JavaScript หลังจากที่หน้าเว็บโหลดแล้ว)
ข้อจำกัด: API นี้จะส่งข้อมูลเป็นสตริงหรืออาร์เรย์ byte[] สำหรับโครงสร้างข้อมูลที่ซับซ้อนมากขึ้น เช่น ออบเจ็กต์ JSON คุณต้องทำให้ข้อมูลนี้เป็นอนุกรมในรูปแบบใดรูปแบบหนึ่ง แล้วจึงยกเลิกการทำอนุกรมในอีกด้านหนึ่งเพื่อสร้างโครงสร้างข้อมูลขึ้นใหม่
ตัวอย่างการใช้งาน
หากต้องการทำความเข้าใจลำดับทั้งหมดของการแลกเปลี่ยนข้อความแบบ 2 ทาง เหตุการณ์ จะดำเนินการตามลำดับต่อไปนี้
- การเริ่มต้น (แอป): แอปเนทีฟจะลงทะเบียน Listener ด้วย
addWebMessageListenerและโหลดหน้าเว็บด้วยloadUrl() - ส่งข้อความ (เว็บ): JavaScript ของหน้าเว็บเรียกใช้
myObject.postMessage(message)เพื่อเริ่มการสื่อสาร - การรับและตอบกลับข้อความ (แอป): แอปจะรับข้อความใน
การเรียกกลับของ Listener และตอบกลับโดยใช้
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 เพื่อส่งเพย์โหลด
ไปยังเฟรมหลักของหน้าเว็บ หากต้องการสร้างช่องทางการสื่อสารแบบ 2 ทาง
คุณสามารถสร้าง WebMessageChannel และส่งพอร์ตหนึ่งของ
พอร์ตนั้นพร้อมข้อความไปยังเนื้อหาเว็บ
ลักษณะ
- อะซิงโครนัส: วิธีนี้ใช้การรับส่งข้อความแบบอะซิงโครนัสเช่นเดียวกับ
addWebMessageListenerซึ่งช่วยให้หน้าเว็บยังคงตอบสนองต่อการโต้ตอบของผู้ใช้ในขณะที่แอปประมวลผลข้อมูลในเบื้องหลัง - รับรู้ต้นทาง: คุณระบุ
targetOriginเพื่อให้มั่นใจว่า WebView จะส่งข้อมูลไปยังเว็บไซต์ที่เชื่อถือได้เท่านั้น
ข้อจำกัด
- ขอบเขต: API นี้จำกัดการสื่อสารไว้ที่เฟรมหลัก แต่ไม่รองรับการระบุที่อยู่หรือการส่งข้อความไปยัง iframe โดยตรง
- ข้อจำกัดของ URI: คุณไม่สามารถใช้วิธีนี้กับเนื้อหาที่โหลดโดยใช้ URI ของ
data:, URI ของfile:หรือloadData()เว้นแต่จะระบุ "*" เป็นต้นทางเป้าหมาย การทำเช่นนี้จะทำให้ทุกหน้าได้รับข้อความ - ความเสี่ยงด้านข้อมูลประจำตัว: เนื้อหาเว็บไม่มีวิธีที่ชัดเจนในการยืนยันตัวตนของผู้ส่ง ข้อความที่หน้าเว็บได้รับอาจ มาจากแอปเนทีฟหรือ iframe อื่น
ใช้วิธีนี้เมื่อต้องการช่องทางแบบอะซิงโครนัสอย่างง่ายสำหรับข้อมูลที่อิงตามสตริงใน Android เวอร์ชันก่อนหน้าที่ไม่รองรับ addWebMessageListener
ใช้ addJavascriptInterface (เดิม)
วิธีที่เก่าแก่ที่สุดคือการแทรกอินสแตนซ์ออบเจ็กต์ดั้งเดิมลงใน WebView โดยตรง
วิธีการทำงาน: คุณกำหนดคลาส Kotlin หรือ Java ใส่คำอธิบายประกอบให้กับเมธอดที่อนุญาตด้วย @JavascriptInterface และเพิ่มอินสแตนซ์ของคลาสลงใน WebView โดยใช้ addJavascriptInterface(Object, String)
ลักษณะ
- ซิงโครนัส: สภาพแวดล้อมการเรียกใช้ JavaScript จะบล็อกจนกว่าเมธอดในโค้ด Android จะส่งคืน
- ความปลอดภัยของเธรด: ระบบจะเรียกใช้เมธอดในเธรดเบื้องหลัง ซึ่งต้องมีการซิงค์อย่างระมัดระวังในฝั่ง Kotlin หรือ Java
- ความเสี่ยงด้านความปลอดภัย: โดยค่าเริ่มต้น
addJavascriptInterfaceจะพร้อมใช้งานใน ทุกเฟรมภายใน WebView รวมถึง iframe ไม่มีการควบคุมการเข้าถึงตามต้นทาง เนื่องจากลักษณะการทำงานแบบไม่พร้อมกันของ WebView จึงไม่สามารถ ระบุ URL ของเฟรมที่เรียกอินเทอร์เฟซของคุณได้อย่างปลอดภัย คุณต้องไม่ใช้วิธีการต่างๆ เช่นWebView.getUrl()เพื่อยืนยันความปลอดภัย เนื่องจากไม่มีการรับประกันความถูกต้องและไม่ได้ระบุว่าเฟรมใดเป็นผู้ส่งคำขอ
สรุปกลไก
ตารางต่อไปนี้แสดงการเปรียบเทียบโดยย่อของกลไกการติดตั้งใช้งานบริดจ์เนทีฟหลัก 3 รายการ
| วิธีการ | addWebMessageListener |
postWebMessage |
addJavascriptInterface |
|---|---|---|---|
| การใช้งาน | อะซิงโครนัส (Listener ในชุดข้อความหลัก) | อะซิงโครนัส | ซิงโครนัส |
| ความปลอดภัย | สูงสุด (อิงตามรายการที่อนุญาต) | สูง (รับรู้ต้นทาง) | ต่ำ (ไม่มีการตรวจสอบต้นทาง) |
| ความซับซ้อน | ปานกลาง | ปานกลาง | เรียบง่าย |
| ทิศทาง | แบบ 2 ทิศทาง | แบบ 2 ทิศทาง | เว็บไปยังแอป |
| เวอร์ชัน WebView ขั้นต่ำ | เวอร์ชัน 82 (และ Jetpack Webkit 1.3.0) | เวอร์ชัน 45 (และ Jetpack Webkit 1.1.0) | ทุกเวอร์ชัน |
| แนะนำ | ใช่ | ไม่ใช่ | ไม่ |
จัดการการโอนข้อมูลขนาดใหญ่
คุณต้องจัดการหน่วยความจำอย่างระมัดระวังเมื่อโอนเพย์โหลดขนาดใหญ่ เช่น สตริงหลายเมกะไบต์หรือไฟล์ไบนารี เพื่อหลีกเลี่ยงข้อผิดพลาด "แอปพลิเคชันไม่ตอบสนอง" (ANR) หรือการขัดข้องในอุปกรณ์ 32 บิต ส่วนนี้จะกล่าวถึงเทคนิคและข้อจำกัดต่างๆ ที่เกี่ยวข้องกับการโอนข้อมูลจำนวนมากระหว่างแอปพลิเคชันโฮสต์กับเนื้อหาเว็บ
โอนข้อมูลไบนารีด้วยอาร์เรย์ไบต์
คลาส WebMessageCompat ช่วยให้คุณส่งอาร์เรย์ byte[] ได้โดยตรง
แทนที่จะแปลงข้อมูลไบนารีเป็นสตริง Base64 เนื่องจาก Base64 เพิ่มค่าใช้จ่ายประมาณ 33% ให้กับขนาดข้อมูล วิธีนี้จึงประหยัดหน่วยความจำและเร็วกว่ามาก
- ข้อดีของไบนารี: โอนข้อมูลไบนารี เช่น ไฟล์รูปภาพหรือเสียง ระหว่าง แอปเนทีฟและเนื้อหาเว็บ
- ข้อจำกัด: แม้จะมีอาร์เรย์ไบต์ แต่ระบบจะคัดลอกข้อมูลข้าม ขอบเขตการสื่อสารระหว่างกระบวนการ (IPC) ระหว่างแอปกับกระบวนการที่แยก ต่างหากซึ่ง WebView ใช้ในการแสดงผลเนื้อหาเว็บ แต่ก็ยังใช้หน่วยความจำ จำนวนมากสำหรับไฟล์ขนาดใหญ่มาก
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่า addWebMessageListener ในฝั่งแอปเนทีฟเพื่อรับข้อความที่ทำเครื่องหมายด้วย WebMessageCompat.TYPE_ARRAY_BUFFER และตอบกลับด้วยข้อมูลไบนารีโดยไม่บังคับด้วยการตรวจสอบ WebViewFeature.MESSAGE_ARRAY_BUFFER
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 ซึ่งช่วยให้เนื้อหาเว็บส่งและรับข้อมูลไบนารี (ArrayBuffer) ไปยังและจากแอปเนทีฟได้โดยใช้พร็อกซี window.myBridge
ที่แทรกในตัวอย่างก่อนหน้า
// 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 เพื่อ
สตรีมข้อมูล
- หน้าเว็บจะเริ่ม
fetch()การเรียกไปยัง URL ตัวยึดตำแหน่งที่กำหนดเอง เช่นhttps://app.local/large-file - แอป Android จะสกัดกั้นคำขอนี้ใน
WebViewClient.shouldInterceptRequest - แอปจะแสดงผลข้อมูลเป็น
InputStream
ซึ่งจะช่วยให้สตรีมข้อมูลเป็นก้อนๆ แทนที่จะโหลดเพย์โหลดทั้งหมด ลงในหน่วยความจำพร้อมกัน
ฟังก์ชัน JavaScript ต่อไปนี้แสดงโค้ดฝั่งไคลเอ็นต์สำหรับ
การโหลดไฟล์ไบนารีขนาดใหญ่จากแอปพลิเคชันเนทีฟอย่างมีประสิทธิภาพโดยใช้
fetch()การเรียกมาตรฐานไปยัง URL ตัวยึดตำแหน่งที่กำหนดเอง
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);
}
}
ตัวอย่างโค้ดต่อไปนี้แสดงฝั่งแอปที่มาพร้อมเครื่องโดยใช้เมธอด
WebViewClient.shouldInterceptRequest ทั้งใน Kotlin และ Java เพื่อสตรีม
ไฟล์ไบนารีขนาดใหญ่โดยการสกัดกั้น 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 ที่ซ่อนอยู่ได้ แต่ก็ไม่สามารถป้องกันช่องโหว่ ของ Cross-Site Scripting (XSS) ภายในโดเมนที่เชื่อถือได้ของคุณเอง เช่น หากหน้าเว็บแสดงเนื้อหาที่ผู้ใช้สร้างขึ้นและมีความเสี่ยงต่อ XSS ที่จัดเก็บไว้ ผู้โจมตีอาจเรียกใช้สคริปต์ที่ทําหน้าที่เป็นต้นทางที่เชื่อถือได้ของคุณ พิจารณา ใช้การตรวจสอบเพย์โหลดของข้อความก่อนดำเนินการที่ละเอียดอ่อน ในแพลตฟอร์มเนทีฟ
ลดพื้นที่ผิว: แสดงเฉพาะเมธอดหรือข้อมูลที่เฉพาะเจาะจงซึ่งหน้าเว็บต้องการเท่านั้น
ตรวจสอบฟีเจอร์ขณะรันไทม์: Bridge API ล่าสุด ซึ่งรวมถึง
addWebMessageListenerเป็นส่วนหนึ่งของไลบรารี Jetpack Webkit ดังนั้น โปรดตรวจสอบว่ามีบริการสนับสนุนโดยใช้WebViewFeature.isFeatureSupported()ก่อนโทรหา