WebViewCompat.navigate เป็นทางเลือกที่ได้รับการปรับปรุงแทน
WebView.loadUrl ซึ่งให้การควบคุมการโหลดหน้าเว็บ การจัดการประวัติ และการติดตามวงจรการนำทางใน WebView ได้อย่างละเอียด
ก่อนหน้านี้ การเริ่มการนำทางหน้าเว็บโดยใช้ loadUrl มีข้อจำกัดที่สำคัญดังนี้
- ไม่มีการแทนที่รายการประวัติ: คุณไม่สามารถแทนที่รายการประวัติปัจจุบันได้ ทำให้ไม่สามารถไปยังหน้าใหม่ได้โดยไม่ต้องเพิ่มรายการลงในสแต็กย้อนกลับ
- การเรียกกลับที่แยกออกจากกัน: ไม่มีกลไกโดยตรงในการเชื่อมโยงการเรียก
loadUrlที่เฉพาะเจาะจงกับเหตุการณ์การเรียกกลับที่ตามมาในWebViewClient - ไม่ได้บันทึกส่วนหัวเพิ่มเติม: ระบบไม่ได้บันทึกส่วนหัวที่กำหนดเองซึ่งส่งไปยัง
loadUrlเป็นส่วนหนึ่งของสถานะWebViewดังนั้นส่วนหัวดังกล่าวจึงหายไปเมื่อกู้คืนสถานะ
WebViewCompat.navigate API แก้ไขปัญหาเหล่านี้ด้วยการเปิดตัวฟีเจอร์ต่อไปนี้
- การแทนที่รายการประวัติการนำทาง: ให้คุณแทนที่หน้าปัจจุบัน
ในสแต็กประวัติ
WebView - การติดตามการเรียกกลับที่เชื่อมโยง: แสดงผลออบเจ็กต์
Navigationที่ ทำหน้าที่เป็นตัวระบุที่ไม่ซ้ำกันในทุกขั้นตอนของวงจรการนำทาง - การรองรับส่วนหัวของสถานะที่บันทึกไว้: ระบบจะบันทึกส่วนหัวเพิ่มเติมไว้ใน
WebViewบันเดิลสถานะอย่างน่าเชื่อถือเพื่อให้ใช้ซ้ำได้เมื่อกู้คืนสถานะ
ความสามารถและข้อจำกัดที่สำคัญ
โปรดพิจารณากฎและข้อจำกัดในการปฏิบัติงานต่อไปนี้ก่อนใช้ WebViewCompat.navigate
ความปลอดภัยของเธรด: คุณต้องเรียกใช้
WebViewCompat.navigateในเธรด UI (หลัก)การยกเลิกและการแทนที่: คุณไม่สามารถยกเลิกการนำทางในเที่ยวบินได้โดยตรง อย่างไรก็ตาม การเริ่ม
navigateการโทรใหม่ในWebViewจะแทนที่การนำทางที่ใช้งานอยู่การรองรับสคีม URI: ระบบรองรับสคีม URI มาตรฐาน (เช่น
https:และhttp:) และที่กำหนดเอง ไม่รองรับรูปแบบjavascript:ขีดจำกัดขนาด URL: ความยาวสตริง URL สูงสุดที่รองรับคือ 2 MB
การตรวจสอบฟีเจอร์: ตรวจสอบความพร้อมใช้งานของฟีเจอร์โดยใช้
WebViewFeature.isFeatureSupportedเสมอก่อนเรียกใช้ API เพื่อ รักษาความเข้ากันได้ใน WebView APK เวอร์ชันต่างๆ
เริ่มการนำทางและติดตามวงจร
หากต้องการกำหนดค่าการนำทางและติดตามวงจร ให้ทำดังนี้
- ลงทะเบียนการติดตั้งใช้งาน
NavigationListenerโดยใช้WebViewCompat.addNavigationListenerระหว่างการตั้งค่าWebViewเพื่อรับ การเรียกกลับวงจรที่มีโครงสร้าง ลงทะเบียน Listener เพียงครั้งเดียว (แทนที่จะลงทะเบียนใน การเรียกการนำทางทุกครั้ง) เพื่อป้องกันหน่วยความจำรั่วและป้องกันการเรียกใช้ การเรียกกลับที่ซ้ำกัน - สร้างอินสแตนซ์
NavigationParametersโดยใช้NavigationParameters.Builderเพื่อระบุลักษณะการทำงานที่ไม่บังคับ เช่น การแทนที่ประวัติหรือส่วนหัว HTTP ที่กำหนดเอง - เรียกใช้
WebViewCompat.navigateโดยส่งอินสแตนซ์WebView, URL ปลายทาง และพารามิเตอร์
WebViewCompat.navigate จะแสดงผลออบเจ็กต์ Navigation ที่ระบุคำขออย่างไม่ซ้ำกัน
ในการเรียกกลับ NavigationListener ให้เปรียบเทียบออบเจ็กต์นี้กับพารามิเตอร์ Navigation ที่เข้ามาเพื่อติดตามการนำทางที่เฉพาะเจาะจงนั้น
ตัวอย่างการใช้งาน
ตัวอย่างต่อไปนี้แสดงวิธีกําหนดค่าพารามิเตอร์การนําทาง
เรียกใช้ WebViewCompat.navigate และรอรับฟังเหตุการณ์วงจรการนําทาง
Kotlin
class WebNavigationManager(private val webView: WebView) {
// Track the navigation instance returned by the API
private var currentNavigation: Navigation? = null
init {
// 1. Define listener to observe navigation lifecycle events
val listener = object : NavigationListener {
override fun onNavigationStarted(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation started
}
}
override fun onNavigationRedirected(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation encountered a redirect
}
}
override fun onNavigationCompleted(navigation: Navigation) {
if (navigation == currentNavigation) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
val statusCode = navigation.statusCode
val error = navigation.webResourceError
}
}
}
override fun onFirstContentfulPaintMillis(page: Page, durationMillis: Long) {
// Match page with current navigation
if (page == currentNavigation?.page) {
// Page rendering started (First Contentful Paint achieved)
}
}
}
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(webView, listener)
}
@UiThread
fun navigateToPage(url: String) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
webView.loadUrl(url)
return
}
// 3. Configure navigation parameters
val params = NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(
mapOf("X-Test-Navigate-Header" to "TestValue")
)
.build()
// 4. Initiate navigation on the UI thread
currentNavigation = WebViewCompat.navigate(webView, url, params)
}
}
Java
public class WebNavigationManager {
private Navigation mCurrentNavigation;
private final WebView mWebView;
public WebNavigationManager(@NonNull WebView webView) {
mWebView = webView;
setupListener();
}
private void setupListener() {
// 1. Define listener to observe navigation lifecycle events
NavigationListener listener = new NavigationListener() {
@Override
public void onNavigationStarted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation started
}
}
@Override
public void onNavigationRedirected(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation encountered a redirect
}
}
@Override
public void onNavigationCompleted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
int statusCode = navigation.getStatusCode();
WebResourceErrorCompat error = navigation.getWebResourceError();
}
}
}
@Override
public void onFirstContentfulPaintMillis(@NonNull Page page, long durationMillis) {
if (mCurrentNavigation != null && page.equals(mCurrentNavigation.getPage())) {
// Page rendering started (First Contentful Paint achieved)
}
}
};
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(mWebView, listener);
}
@UiThread
public void navigateToPage(@NonNull String url) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
mWebView.loadUrl(url);
return;
}
// 3. Configure navigation parameters
NavigationParameters params = new NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(Collections.singletonMap(
"X-Test-Navigate-Header", "TestValue"
))
.build();
// 4. Initiate navigation on the UI thread
mCurrentNavigation = WebViewCompat.navigate(mWebView, url, params);
}
}
เผยแพร่สถานะแอปโดยใช้ส่วนหัว HTTP
เว็บแอปมักต้องใช้บริบทจากแอป Android โฮสต์เพื่อประสานงานตรรกะแบ็กเอนด์ หรือปรับแต่งเนื้อหาเว็บ การต่อท้ายพารามิเตอร์การค้นหาใน URL เพื่อส่งข้อมูลนี้อาจทำให้ URL รก รบกวนการแคช และเปิดเผยสถานะแอปภายใน
เราขอแนะนำให้ส่งบริบทของแอปโดยใช้ส่วนหัว HTTP ที่กำหนดเองแทน การใช้
WebViewCompat.navigate และ NavigationParameters จะช่วยให้คุณส่งข้อมูลนี้ไปยังเซิร์ฟเวอร์ได้อย่างปลอดภัย
นอกจากนี้ WebView ยังคงเก็บส่วนหัวเหล่านี้ไว้ในระหว่างการคืนค่าสถานะ ซึ่งช่วยให้เนื้อหาเว็บยังคงสอดคล้องกันเมื่อมีการเปลี่ยนแปลงการกำหนดค่า โปรดทราบว่าการคงอยู่นี้จะมีผลเมื่อใช้
WebViewCompat.navigateเท่านั้น หากคุณใช้ WebView.loadUrl ระบบจะไม่บันทึกส่วนหัวที่กำหนดเองในแพ็กเกจสถานะ WebView และจะหายไปเมื่อมีการกู้คืน
กรณีการใช้งานทั่วไป
กรณีการใช้งานทั่วไปสำหรับการส่งบริบทของแอปโฮสต์มีดังนี้
- เวอร์ชันของแอป (
X-App-Version): การส่งเวอร์ชันที่เผยแพร่ของแอปโฮสต์ (เช่นBuildConfig.VERSION_NAME) จะช่วยให้เซิร์ฟเวอร์แบ็กเอนด์ยืนยันความเข้ากันได้ของบริดจ์ JavaScript ดั้งเดิม, ฟีเจอร์ที่ต้องมีสิทธิ์ หรือแจ้งให้ผู้ใช้อัปเดตแอปเวอร์ชันเก่าได้ - แพลตฟอร์มไคลเอ็นต์ (
X-Client-Platform): การระบุสภาพแวดล้อมโฮสต์อย่างชัดเจนเป็น Android จะช่วยให้เซิร์ฟเวอร์แสดง UI ที่ปรับให้เหมาะกับแพลตฟอร์มหรือลิงก์ร้านค้าได้โดยไม่ต้องอาศัยการแยกวิเคราะห์สตริงUser-Agent
ตัวอย่างการใช้งาน
ตัวอย่างต่อไปนี้แสดงวิธีส่งเวอร์ชันแอปพลิเคชันและแพลตฟอร์มไคลเอ็นต์ไปยังเว็บเซิร์ฟเวอร์
Kotlin
// Attach host app metadata so the server can verify compatibility and tailor content
val params = NavigationParameters.Builder()
.addAdditionalHeaders(
mapOf(
"X-App-Version" to BuildConfig.VERSION_NAME,
"X-Client-Platform" to "Android"
)
)
.build()
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params)
Java
// Attach host app metadata so the server can verify compatibility and tailor content
Map<String, String> headers = new HashMap<>();
headers.put("X-App-Version", BuildConfig.VERSION_NAME);
headers.put("X-Client-Platform", "Android");
NavigationParameters params = new NavigationParameters.Builder()
.addAdditionalHeaders(headers)
.build();
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params);
โหมดความล้มเหลวและการจัดการข้อผิดพลาด
WebViewCompat.navigate API มีกลไกที่แตกต่างกันในการจัดการ
ข้อผิดพลาดในการกำหนดค่าและการนำทางรันไทม์ที่ล้มเหลว ดังนี้
ข้อยกเว้นของอาร์กิวเมนต์ที่ไม่ถูกต้อง
การส่งอาร์กิวเมนต์ที่ไม่ถูกต้องจะทริกเกอร์ IllegalArgumentException แบบซิงโครนัส
สาเหตุที่พบบ่อย ได้แก่
- ส่ง
nullสำหรับพารามิเตอร์ที่จำเป็นซึ่งไม่ใช่ค่าว่าง (webView,urlหรือparams) - การระบุรูปแบบ URL ที่ไม่รองรับ เช่น
javascript: - การส่งคีย์หรือค่าส่วนหัว HTTP ที่มีรูปแบบไม่ถูกต้องซึ่งไม่เป็นไปตามข้อกำหนดของ RFC 2616
ข้อผิดพลาดในกระบวนการนำทาง
หากเกิดข้อผิดพลาดระหว่างคำขอเครือข่ายหรือการโหลดหน้าเว็บ (เช่น รหัสสถานะ HTTP 404
, การแปลง DNS ล้มเหลว หรือข้อผิดพลาด SSL) WebViewCompat.navigate
จะยังคงแสดงออบเจ็กต์ Navigation ที่ถูกต้อง
เมื่อการนำทางเสร็จสิ้น ให้ตรวจสอบเมธอดต่อไปนี้ในอินสแตนซ์ Navigation
ภายในการเรียกกลับ onNavigationCompleted เพื่อวินิจฉัยความล้มเหลว
getStatusCode: แสดงรหัสสถานะการตอบกลับ HTTP (เช่น404หรือ500)getWebResourceError: แสดงผลออบเจ็กต์WebResourceErrorCompatที่แสดงรายละเอียดข้อผิดพลาดของเครือข่าย เช่น การหมดเวลาการเชื่อมต่อหรือการค้นหาโฮสต์ ล้มเหลวdidCommitErrorPage: ระบุว่าWebViewยืนยันและ แสดงหน้าข้อผิดพลาดต่อผู้ใช้หรือไม่didCommit: ระบุว่าการนำทางไปยังหน้าเป้าหมายสำเร็จหรือไม่โดยไม่มีการยกเลิก
การจัดการชุดบันทึกสถานะ
เมื่อส่งส่วนหัวเพิ่มเติมด้วย NavigationParameters WebView จะบันทึกส่วนหัวเหล่านี้ไว้ในแพ็กเกจสถานะที่บันทึกไว้เพื่อให้ใช้ซ้ำได้เมื่อมีการกู้คืนสถานะ อย่างไรก็ตาม คอลเล็กชันส่วนหัวขนาดใหญ่อาจเพิ่มขนาดของสถานะที่บันทึกไว้ Bundle ได้อย่างมาก
หากต้องการจำกัดขนาดของ Bundle เพื่อป้องกัน TransactionTooLargeException
ระหว่างการบันทึกสถานะของ Android ให้ใช้ WebViewCompat.saveState เมธอดนี้
ช่วยให้คุณกำหนดขีดจำกัดขนาดสูงสุดของแพ็กเกจเป็นไบต์ และเลือกที่จะยกเว้นรายการประวัติการส่งต่อได้
Kotlin
// Save state with a maximum bundle size limit (for example, 64 KB)
val maxSizeBytes = 64 * 1024
val includeForwardState = false
val outState = Bundle()
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState)
Java
// Save state with a maximum bundle size limit (for example, 64 KB)
int maxSizeBytes = 64 * 1024;
boolean includeForwardState = false;
Bundle outState = new Bundle();
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState);
ไฟล์ที่ได้จะยังคงใช้ร่วมกับวิธีWebView.restoreState มาตรฐานได้
คำแนะนำในการย้ายข้อมูลและการติดตั้งใช้งาน
หากต้องการให้การนำทางใน WebView มีประสิทธิภาพและความเสถียรสูงสุด
โปรดปฏิบัติตามคำแนะนำต่อไปนี้
ย้ายข้อมูลจาก
loadUrlไปยังnavigate: ย้ายข้อมูลการเรียกใช้WebView.loadUrlรุ่นเดิมทั้งหมดไปยังWebViewCompat.navigateซึ่งจะช่วยให้การจัดการประวัติเป็นไปอย่างสม่ำเสมอ และช่วยให้มั่นใจได้ว่าระบบจะบันทึกส่วนหัวเป็นส่วนหนึ่งของ สถานะที่บันทึกไว้เสมอตรวจสอบการรองรับฟีเจอร์เสมอ: ก่อนเรียกใช้ API ให้ยืนยัน การรองรับรันไทม์กับ
WebViewFeature.isFeatureSupportedเพื่อป้องกัน WebView เวอร์ชันเก่าเชื่อมโยงอินสแตนซ์การนำทาง: ใช้ออบเจ็กต์
Navigationที่ส่งคืนเพื่อ แยกความแตกต่างของการนำทางพร้อมกันหรือตัวเรียกกลับของตัวกรองเมื่อจัดการ อินสแตนซ์WebViewหลายรายการลงทะเบียน Listener เพียงครั้งเดียวในระหว่างการเริ่มต้น: เนื่องจาก
WebViewCompat.addNavigationListenerจะเพิ่ม Listener แทนที่จะแทนที่ Listener ที่มีอยู่ ให้ลงทะเบียนNavigationListenerเพียงครั้งเดียวในระหว่างการตั้งค่าWebViewเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำและการเรียกใช้ฟังก์ชันเรียกกลับที่ซ้ำกันในการนำทางครั้งต่อๆ ไปตรวจสอบขนาดสถานะการบันทึก: เมื่อส่งเพย์โหลดส่วนหัวขนาดใหญ่ ให้ใช้
WebViewCompat.saveStateที่มีขอบเขตขนาดที่ชัดเจนเพื่อหลีกเลี่ยงการบันทึก ข้อมูลสถานะมากเกินไป
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับความสามารถของเว็บที่ฝังไว้และการเพิ่มประสิทธิภาพได้ในคำแนะนำต่อไปนี้
- ทำให้การใช้งาน WebView ง่ายขึ้นด้วย Jetpack Webkit
- การโหลดแบบคาดเดาใน WebView
- เพิ่มประสิทธิภาพการเริ่มต้น WebView
- จัดการการสิ้นสุดกระบวนการแสดงผล WebView