WebViewCompat.navigate 是 WebView.loadUrl 的強化替代方案,可精細控管網頁載入、記錄管理,以及 WebView 中的導覽生命週期追蹤。
先前使用 loadUrl 啟動網頁導覽時,有下列顯著限制:
- 無法取代記錄項目:您無法取代目前的記錄項目,因此必須將項目新增至返回堆疊,才能前往新網頁。
- 回呼已解除耦合:在
WebViewClient中,沒有直接機制可將特定loadUrl呼叫與後續回呼事件建立關聯。 - 未儲存額外標頭:傳遞至
loadUrl的自訂標頭未儲存為WebView狀態的一部分,因此還原狀態時會遺失。
WebViewCompat.navigate API 導入下列功能,解決這些問題:
- 取代導覽記錄項目:可讓您取代
WebView記錄堆疊中的目前頁面。 - 相關回呼追蹤:傳回
Navigation物件,做為導覽生命週期所有階段的專屬 ID。 - 支援儲存狀態標頭:額外標頭會可靠地儲存在
WebView狀態套件中,以便在還原狀態時重複使用。
主要功能和限制
採用 WebViewCompat.navigate 前,請先考量下列作業規則和限制:
執行緒安全:您必須在 UI (主) 執行緒上叫用
WebViewCompat.navigate。取消和優先順序:無法明確取消進行中的導覽。不過,在同一個
WebView上啟動新的navigate呼叫,會取代任何有效的導覽。支援 URI 配置:支援標準 (例如
https:和http:) 和自訂 URI 配置。系統不支援javascript:配置。網址大小限制:支援的網址字串長度上限為 2 MB。
功能檢查:請務必先使用
WebViewFeature.isFeatureSupported檢查功能是否可用,再叫用 API,確保不同 WebView APK 版本之間的相容性。
啟動導航並追蹤生命週期
如要設定導覽並追蹤其生命週期,請按照下列步驟操作:
- 在
WebView設定期間,使用WebViewCompat.addNavigationListener註冊NavigationListener實作項目,即可接收結構化生命週期回呼。註冊監聽器一次 (而非每次導覽呼叫時),避免發生記憶體洩漏和重複執行回呼的情況。 - 使用
NavigationParameters.Builder建構NavigationParameters執行個體,指定選用行為,例如取代記錄或自訂 HTTP 標頭。 - 呼叫
WebViewCompat.navigate,並傳遞WebView執行個體、到達網頁網址和參數。
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);
}
}
故障模式和錯誤處理
WebViewCompat.navigate API 提供不同的機制,可處理設定錯誤和執行階段導覽失敗:
引數無效例外狀況
傳遞無效引數會觸發同步 IllegalArgumentException。
常見原因包括:
- 針對必要非空值參數 (
webView、url或params) 傳遞null。 - 提供不支援的網址配置,例如
javascript:。 - 傳遞格式錯誤的 HTTP 標頭鍵或值,不符合 RFC 2616 規格。
導覽程序錯誤
如果網路要求或載入網頁期間發生失敗 (例如 HTTP 404 狀態碼、DNS 解析失敗或 SSL 錯誤),WebViewCompat.navigate 仍會傳回有效的 Navigation 物件。
導覽完成後,請檢查 onNavigationCompleted 回呼內 Navigation 執行個體上的下列方法,診斷失敗原因:
getStatusCode:傳回 HTTP 回應狀態碼 (例如404或500)。getWebResourceError:傳回WebResourceErrorCompat物件,詳細說明網路錯誤,例如連線逾時或主機查閱失敗。didCommitErrorPage:指出WebView是否已提交並向使用者顯示錯誤頁面。didCommit:指出導覽是否成功提交至目標網頁,且未遭到中止。
儲存狀態套件管理
使用 NavigationParameters 傳遞額外標頭時,WebView 會將這些標頭儲存在已儲存的狀態套件中,以便在還原狀態時重複使用。不過,如果標頭數量龐大,儲存狀態 Bundle 的大小可能會大幅增加。
如要限制套裝組合大小,避免在 Android 狀態儲存期間發生 TransactionTooLargeException,請使用 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。關聯導覽例項:管理多個
WebView例項時,使用傳回的Navigation物件區分並行導覽或篩選回呼。在初始化期間註冊一次監聽器:由於
WebViewCompat.addNavigationListener會新增監聽器,而不是取代現有監聽器,因此請在WebView設定期間註冊一次NavigationListener,避免記憶體洩漏,以及在後續導覽中重複執行回呼。監控儲存狀態大小:傳遞大型標頭酬載時,請使用
WebViewCompat.saveState並明確指定大小界線,避免儲存過多的狀態資料。
其他資源
如要進一步瞭解內嵌網頁功能和效能最佳化,請參閱下列指南: