WebViewCompat.navigate ist eine erweiterte Alternative zu
WebView.loadUrl, mit der Sie die Steuerung des Ladens von Webseiten, der Verlaufsverwaltung und des Tracking des Navigationslebenszyklus in WebView detailliert steuern können.
Bisher gab es bei der Initiierung der Seitennavigation mit loadUrl erhebliche Einschränkungen:
- Kein Ersetzen von Verlaufseinträgen:Sie konnten den aktuellen Verlaufseintrag nicht ersetzen. Daher war es nicht möglich, zu einer neuen Seite zu navigieren, ohne dem Back-Stack einen Eintrag hinzuzufügen.
- Entkoppelte Callbacks:Es gab keinen direkten Mechanismus, um einen bestimmten
loadUrl-Aufruf mit nachfolgenden Callback-Ereignissen inWebViewClientzu korrelieren. - Zusätzliche Header nicht gespeichert:Benutzerdefinierte Header, die an
loadUrlübergeben wurden, wurden nicht als Teil desWebView-Status gespeichert. Sie gingen also beim Wiederherstellen des Status verloren.
Die WebViewCompat.navigate API behebt diese Probleme durch die Einführung der folgenden Funktionen:
- Ersetzen von Navigationsverlaufseinträgen:Ermöglicht das Ersetzen der aktuellen Seite im
WebView-Verlaufsstack. - Korreliertes Callback-Tracking: Gibt ein
Navigation-Objekt zurück, das als eindeutige ID für alle Phasen eines Navigationslebenszyklus dient. - Unterstützung für gespeicherte Statusheader:Zusätzliche Header werden zuverlässig im
WebView-Statusbundle gespeichert, sodass sie bei der Statuswiederherstellung wiederverwendet werden können.
Wichtige Funktionen und Einschränkungen
Beachten Sie vor der Einführung von WebViewCompat.navigate die folgenden Betriebsregeln und Einschränkungen:
Threadsicherheit:Sie müssen
WebViewCompat.navigateim UI-Thread (Hauptthread) aufrufen.Abbruch und Vorrang:Aktive Navigationen können nicht explizit abgebrochen werden. Wenn Sie jedoch einen neuen
navigate-Aufruf für dasselbeWebViewinitiieren, wird jede aktive Navigation überschrieben.Unterstützung für URI-Schemas:Standard- (z. B.
https:undhttp:) und benutzerdefinierte URI-Schemas werden unterstützt. Dasjavascript:-Schema wird nicht unterstützt.URL-Größenbeschränkung:Die maximal unterstützte Länge des URL-Strings beträgt 2 MB.
Funktionsprüfung: Prüfen Sie immer die Verfügbarkeit von Funktionen mit
WebViewFeature.isFeatureSupported, bevor Sie die API aufrufen, um die Kompatibilität mit verschiedenen WebView-APK-Versionen zu gewährleisten.
Navigation initiieren und Lebenszyklus verfolgen
So konfigurieren Sie die Navigation und verfolgen ihren Lebenszyklus:
- Registrieren Sie während der
WebViewEinrichtung eineNavigationListenerImplementierung mitWebViewCompat.addNavigationListener, um strukturierte Lebenszyklus-Callbacks zu erhalten. Registrieren Sie den Listener einmal (nicht bei jedem Navigationsaufruf), um Speicherlecks und doppelte Callback-Ausführungen zu vermeiden. - Erstellen Sie mit
NavigationParameters.BuildereineNavigationParameters-Instanz, um optionale Verhaltensweisen wie das Ersetzen des Verlaufs oder benutzerdefinierte HTTP-Header anzugeben. - Rufen Sie
WebViewCompat.navigateauf und übergeben Sie IhreWebViewInstanz, die Ziel-URL und die Parameter.
WebViewCompat.navigate gibt ein Navigation-Objekt zurück, das die Anfrage eindeutig
identifiziert. Vergleichen Sie dieses Objekt in Ihren NavigationListener-Callbacks mit dem eingehenden Navigation-Parameter, um diese bestimmte Navigation zu verfolgen.
Implementierungsbeispiel
Im folgenden Beispiel wird gezeigt, wie Sie Navigationsparameter konfigurieren, WebViewCompat.navigate aufrufen und auf Ereignisse im Navigationslebenszyklus warten:
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);
}
}
Fehlermodi und Fehlerbehandlung
Die WebViewCompat.navigate API bietet verschiedene Mechanismen zur Behandlung von Konfigurationsfehlern und Laufzeitfehlern bei der Navigation:
Ausnahmen für ungültige Argumente
Wenn Sie ungültige Argumente übergeben, wird eine synchrone IllegalArgumentException ausgelöst.
Häufige Ursachen sind:
nullfür erforderliche Parameter, die nicht null sein dürfen, übergeben (webView,urloderparams).- Ein nicht unterstütztes URL-Schema angeben, z. B.
javascript:. - Falsch formatierte HTTP-Headerschlüssel oder -werte übergeben, die nicht den RFC 2616-Spezifikationen entsprechen.
Fehler im Navigationsprozess
Wenn während der Netzwerkanfrage oder des Seitenaufbaus ein Fehler auftritt (z. B. ein HTTP 404 Statuscode, ein Fehler bei der DNS-Auflösung oder ein SSL-Fehler), gibt WebViewCompat.navigate trotzdem ein gültiges Navigation-Objekt zurück.
Wenn die Navigation abgeschlossen ist, prüfen Sie die folgenden Methoden für die Navigation
Instanz in Ihrem onNavigationCompleted Callback, um den
Fehler zu diagnostizieren:
getStatusCode: Gibt den HTTP-Antwortstatuscode zurück (z. B.404oder500).getWebResourceError: Gibt einWebResourceErrorCompatObjekt zurück, das Netzwerkfehler wie Verbindungszeitüberschreitungen oder Fehler bei der Hostsuche beschreibt.didCommitErrorPage: Gibt an, obWebVieweine Fehlerseite an den Nutzer gesendet und angezeigt hat.didCommit: Gibt an, ob die Navigation erfolgreich auf einer Zielseite ausgeführt wurde, ohne abgebrochen zu werden.
Statusbundle-Verwaltung speichern
Wenn Sie zusätzliche Header mit NavigationParameters übergeben, speichert WebView diese Header im Statusbundle, sodass sie bei der Statuswiederherstellung wiederverwendet werden können. Große Header-Sammlungen können jedoch die Größe des gespeicherten Status-Bundle erheblich erhöhen.
Wenn Sie die Bundle-Größe beschränken müssen, um TransactionTooLargeException
beim Speichern des Android-Status zu vermeiden, verwenden Sie WebViewCompat.saveState. Mit dieser Methode können Sie eine maximale Bundle-Größe in Byte festlegen und optional Elemente aus dem Vorwärtsverlauf ausschließen:
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);
Das resultierende Bundle ist weiterhin mit der Standardmethode
WebView.restoreState kompatibel.
Empfehlungen für die Migration und Implementierung
Folgen Sie diesen Empfehlungen, um eine optimale Leistung und Stabilität bei der Navigation in WebView zu gewährleisten:
Von
loadUrlzunavigatemigrieren:Migrieren Sie alle Legacy-WebView.loadUrl-Aufrufe zuWebViewCompat.navigate. So wird eine einheitliche Verlaufsverwaltung gewährleistet und Header werden immer als Teil des gespeicherten Status gespeichert.Immer die Funktionsunterstützung prüfen:Prüfen Sie vor dem Aufrufen der API die Laufzeitunterstützung mit
WebViewFeature.isFeatureSupported, um Probleme mit älteren WebView-Versionen zu vermeiden.Navigationsinstanzen korrelieren:Verwenden Sie das zurückgegebene
Navigation-Objekt, um gleichzeitige Navigationen zu unterscheiden oder Callbacks zu filtern, wenn Sie mehrereWebView-Instanzen verwalten.Listener einmal bei der Initialisierung registrieren: Da
WebViewCompat.addNavigationListenereinen Listener hinzufügt, anstatt einen vorhandenen zu ersetzen, registrieren Sie IhrenNavigationListenereinmal während derWebView-Einrichtung, um Speicherlecks und doppelte Callback-Ausführungen bei nachfolgenden Navigationen zu vermeiden.Größe des gespeicherten Status überwachen:Wenn Sie große Header-Nutzlasten übergeben, verwenden Sie
WebViewCompat.saveStatemit expliziten Größenbeschränkungen, um zu vermeiden, dass zu viele Statusdaten gespeichert werden.
Zusätzliche Ressourcen
Weitere Informationen zu eingebetteten Webfunktionen und zur Leistungsoptimierung finden Sie in den folgenden Leitfäden:
- WebView-Implementierung mit Jetpack Webkit vereinfachen
- Spekulatives Laden in WebView
- WebView-Start optimieren
- Behandlung der Beendigung des WebView-Rendererprozesses