WebViewCompat.navigate ist eine erweiterte Alternative zu WebView.loadUrl, mit der Sie das Laden von Webseiten, die Verlaufsverwaltung und das Tracking des Navigationslebenszyklus in WebView detailliert steuern können.
Bisher gab es bei der Initiierung von Seitennavigationen mit loadUrl erhebliche Einschränkungen:
- Kein Ersetzen von Verlaufseinträgen:Der aktuelle Verlaufseintrag konnte nicht ersetzt werden. Daher war es nicht möglich, zu einer neuen Seite zu wechseln, ohne dem Backstack 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. Daher gingen sie beim Wiederherstellen des Status verloren.
Die WebViewCompat.navigate API behebt diese Probleme durch die Einführung der folgenden Funktionen:
- Eintrag im Navigationsverlauf ersetzen:Damit können Sie die aktuelle Seite im
WebView-Verlauf ersetzen. - Korrelierte Callback-Analyse:Gibt ein
Navigation-Objekt zurück, das als eindeutige Kennzeichnung für alle Phasen eines Navigationslebenszyklus dient. - Unterstützung von Headern für gespeicherten Zustand:Zusätzliche Header werden zuverlässig im
WebView-Zustands-Bundle gespeichert, damit sie beim Wiederherstellen des Zustands 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.Abbrechen und Vorrang:Laufende Navigationen können nicht explizit abgebrochen werden. Wenn Sie jedoch einen neuen
navigate-Anruf auf demselbenWebViewstarten, wird die aktive Navigation überschrieben.Unterstützung von URI-Schemas:Es werden Standard-URI-Schemas (z. B.
https:undhttp:) und benutzerdefinierte URI-Schemas unterstützt. Das Schemajavascript: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 starten und Track-Lebenszyklus verfolgen
So konfigurieren Sie die Navigation und verfolgen ihren Lebenszyklus:
- Registrieren Sie eine
NavigationListener-Implementierung mitWebViewCompat.addNavigationListenerwährend derWebView-Einrichtung, um strukturierte Lebenszyklus-Callbacks zu erhalten. Registrieren Sie den Listener nur einmal (nicht bei jedem Navigationsaufruf), um Speicherlecks und doppelte Callback-Ausführungen zu vermeiden. - Erstellen Sie eine
NavigationParameters-Instanz mitNavigationParameters.Builder, um optionales Verhalten wie das Ersetzen des Verlaufs oder benutzerdefinierte HTTP-Header anzugeben. - Rufen Sie
WebViewCompat.navigateauf und übergeben Sie IhreWebView-Instanz, 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 erfassen.
Implementierungsbeispiel
Das folgende Beispiel zeigt, wie Navigationsparameter konfiguriert, WebViewCompat.navigate aufgerufen und auf Navigationslebenszyklusereignisse gewartet wird:
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);
}
}
App-Status mit HTTP-Headern weitergeben
Web-Apps benötigen oft Kontext von der Host-Android-App, um die Backend-Logik zu koordinieren oder Webinhalte anzupassen. Wenn Sie Suchparameter an die URL anhängen, um diese Informationen zu übergeben, kann das zu unübersichtlichen URLs führen, das Caching beeinträchtigen und den internen App-Status offenlegen.
Wir empfehlen stattdessen, den App-Kontext über benutzerdefinierte HTTP-Header zu übergeben. Mit WebViewCompat.navigate und NavigationParameters können Sie diese Daten sicher an Ihren Server senden. Außerdem werden diese Header von WebView beim Wiederherstellen des Status beibehalten. So wird dafür gesorgt, dass die Webinhalte bei Konfigurationsänderungen konsistent bleiben. Diese Persistenz gilt nur, wenn WebViewCompat.navigate verwendet wird. Wenn Sie WebView.loadUrl verwenden, werden benutzerdefinierte Header nicht im WebView-Status-Bundle gespeichert und gehen bei der Wiederherstellung verloren.
Gängige Anwendungsfälle
Häufige Anwendungsfälle für das Übergeben des Host-App-Kontexts:
- App-Version (
X-App-Version): Wenn Sie die Release-Version der Host-App (z. B.BuildConfig.VERSION_NAME) übergeben, kann Ihr Backend-Server die Kompatibilität der nativen JavaScript-Bridge überprüfen, Funktionen einschränken oder Nutzer auffordern, ältere Apps zu aktualisieren. - Clientplattform (
X-Client-Platform): Wenn die Hostumgebung explizit als Android angegeben wird, kann der Server plattformspezifische Benutzeroberflächen bereitstellen oder Store-Links weiterleiten, ohne dass derUser-Agent-String geparst werden muss.
Implementierungsbeispiel
Das folgende Beispiel zeigt, wie die Anwendungsversion und die Clientplattform an einen Webserver übergeben werden:
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);
Fehlermodi und Fehlerbehandlung
Die WebViewCompat.navigate API bietet unterschiedliche Mechanismen für die Verarbeitung von Konfigurationsfehlern und Laufzeitnavigationsfehlern:
Ausnahmen für ungültige Argumente
Wenn ungültige Argumente übergeben werden, wird ein synchrones IllegalArgumentException ausgelöst.
Häufige Ursachen sind:
nullfür erforderliche Parameter übergeben, die nicht null sein dürfen (webView,urloderparams).- Sie geben ein nicht unterstütztes URL-Schema an, z. B.
javascript:. - Übergeben von fehlerhaften HTTP-Headerschlüsseln oder -werten, die nicht den Spezifikationen von RFC 2616 entsprechen.
Fehler bei der Navigation
Wenn während der Netzwerkanfrage oder des Seitenaufrufs ein Fehler auftritt (z. B. ein HTTP-Statuscode 404, ein DNS-Auflösungsfehler oder ein SSL-Fehler), gibt WebViewCompat.navigate weiterhin ein gültiges Navigation-Objekt zurück.
Wenn die Navigation abgeschlossen ist, untersuchen 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 einWebResourceErrorCompat-Objekt mit Details zu Netzwerkfehlern zurück, z. B. Verbindungszeitüberschreitungen oder Fehler bei der Hostsuche.didCommitErrorPage: Gibt an, obWebViewübernommen und dem Nutzer eine Fehlerseite angezeigt wurde.didCommit: Gibt an, ob die Navigation erfolgreich auf einer Zielseite ausgeführt wurde, ohne abgebrochen zu werden.
Verwaltung von Status-Bundles speichern
Wenn Sie zusätzliche Header mit NavigationParameters übergeben, speichert WebView diese Header im gespeicherten Status-Bundle, damit sie beim Wiederherstellen des Status wiederverwendet werden können. Große Sammlungen von Headern können jedoch die Größe des gespeicherten Status Bundle erheblich erhöhen.
Wenn Sie die Bundle-Größe einschränken müssen, um TransactionTooLargeException beim Speichern des Android-Status zu verhindern, verwenden Sie WebViewCompat.saveState. Mit dieser Methode können Sie ein maximales Limit für die 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
Damit Sie in WebView eine optimale Leistung und Stabilität erzielen, sollten Sie die folgenden Empfehlungen beachten:
Von
loadUrlzunavigatemigrieren:Migrieren Sie alle Legacy-WebView.loadUrl-Aufrufe zuWebViewCompat.navigate. So wird eine einheitliche Verlaufsverwaltung gewährleistet und dafür gesorgt, dass Kopfzeilen immer als Teil des gespeicherten Status gespeichert werden.Funktionsunterstützung immer prüfen:Bevor Sie die API aufrufen, prüfen Sie mit
WebViewFeature.isFeatureSupported, ob die Laufzeitumgebung unterstützt wird, um Probleme mit älteren WebView-Versionen zu vermeiden.Navigationsinstanzen korrelieren:Mit dem zurückgegebenen
Navigation-Objekt können Sie gleichzeitige Navigationen unterscheiden oder Rückrufe filtern, wenn Sie mehrereWebView-Instanzen verwalten.Listener einmal während der Initialisierung registrieren:Da mit
WebViewCompat.addNavigationListenerein Listener hinzugefügt und nicht ein vorhandener ersetzt wird, sollten SieNavigationListenereinmal während derWebView-Einrichtung registrieren, um Speicherlecks und doppelte Callback-Ausführungen bei nachfolgenden Navigationsvorgängen 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
- Beenden des WebView-Rendererprozesses verarbeiten