Die Navigationslatenz ist ein wichtiger Messwert für die Nutzerfreundlichkeit. Um Entwicklern zu helfen, diese Latenz zu verringern, bietet WebView APIs für das spekulative Laden. So kann Ihre App Inhalte abrufen oder rendern, bevor der Nutzer explizit dorthin navigiert.
WebView unterstützt drei primäre Arten des spekulativen Ladens: Preconnect, Prefetch und Prerender.
Durch die Implementierung einer spekulativen Ladestrategie können Sie Folgendes erreichen:
- Deutliche Reduzierung der Latenz beim Laden von Webinhalten:Verlegen Sie den Netzwerkstartzeitpunkt auf einen früheren Zeitpunkt im App-Lebenszyklus.
- Höhere Erfolgsraten bei der Navigation:Durch das Vorbereiten des Netzwerks und des Cache ist die Wahrscheinlichkeit geringer, dass die Navigation aufgrund vorübergehender Netzwerkprobleme fehlschlägt.
- Bessere wahrgenommene Reaktionsfähigkeit:Insbesondere durch das Pre-Rendering werden sofortige Übergänge ermöglicht, wodurch sich die App deutlich schneller anfühlt.
Spekulatives Laden auswählen
Der Hauptunterschied zwischen diesen Strategien liegt in ihrem Umfang: Die Preconnect API ist ursprungsbasiert, d. h., sie erfordert nur die Zieldomain. Prefetch- und Prerender-APIs sind URL-basiert. Das bedeutet, dass der genaue Pfad der Webseite erforderlich ist.
Da Preconnect auf Ursprungsebene funktioniert, kann es viel früher im App-Lebenszyklus initiiert werden, noch bevor Sie wissen, zu welchen Inhalten oder Seiten der Nutzer navigieren wird.
In der folgenden Tabelle werden diese drei Strategien verglichen, damit Sie die richtige für Ihren Anwendungsfall auswählen können:
| Funktion | Vorverbindungen herstellen | Prefetch | Vorab rendern |
|---|---|---|---|
| Hauptziel | Verbindung aufbauen | Nur HTML im Cache speichern (ohne JavaScript oder CSS) | Die gesamte Seite vorrendern |
| Einsatzbereich | Profilebene (gemeinsam für WebViews) | Profilebene (gemeinsam für WebViews) | WebView-Ebene (an eine bestimmte WebView gebunden) |
| Jetpack WebKit API | androidx.webkit.Profile |
androidx.webkit.Profile |
androidx.webkit.WebViewCompat |
| Wichtige API-Methoden | preconnect(...) |
prefetchUrlAsync(...) |
prerenderUrlAsync(...) |
| Configuration | – | PrefetchCache.setMaxPrefetches()PrefetchCache.setPrefetchTtlSeconds() |
setMaxPrerenders() |
| Ressourcennutzung | Niedrig (Netzwerk) | Medium (Netzwerk, Arbeitsspeicher) | Hoch (CPU, Arbeitsspeicher, Netzwerk) |
| Verwendung | Wenn die Zielursprungsadresse bekannt ist, die spezifische URL aber noch nicht festgelegt wurde. | Wenn die genaue URL bekannt ist und die Navigation wahrscheinlich ist, wobei das Caching für alle WebViews freigegeben wird. | Wenn die genaue URL bekannt ist und die Navigation in einer bestimmten WebView sehr wahrscheinlich ist. |
| Vorteile | Schnellere Verbindungsherstellung für jede URL auf dem Ursprung | Schnelleres Laden von Netzwerkressourcen für übereinstimmende URLs | Sofortige Navigation nach der Aktivierung |
Vorverbindungen zu Ursprüngen herstellen
Durch das Herstellen einer Vorabverbindung werden zukünftige Ladevorgänge beschleunigt, indem DNS-Lookups und TCP/TLS-Handshakes für einen bestimmten Ursprung präventiv ausgeführt werden.
Im Gegensatz zu Prefetch und Prerender, für die eine genaue Ziel-URL erforderlich ist, basiert Preconnect ausschließlich auf dem Ursprung. So können Sie den Preconnect-Aufruf viel früher als Prefetch und Prerender ausführen.
Diese ressourcenarme Strategie auf Profilebene reduziert die anfängliche Latenz für alle WebView-Freigaben dieses Profils, sofern die Herkunft noch nicht besucht wurde. Die Verbindung bleibt etwa 30 Sekunden lang offen. Das ist von Vorteil für alle nachfolgenden HTTP-Anfragen, Navigationsvorgänge oder untergeordneten Ressourcen, da der Handshake-Overhead entfällt.
Implementierung
Rufen Sie preconnect(String url) auf einer Profile-Instanz auf, um eine Vorabverbindung herzustellen. Diese API muss im UI-Thread aufgerufen werden und erfordert die Unterstützung von WebViewFeature.PRECONNECT.
Die API arbeitet mit dem Ursprung, aber zur Vereinfachung kann eine vollständige URL angegeben werden (z. B. https://www.example.com/index.html). Diese wird automatisch als Aufruf des Ursprungs behandelt (z. B. https://www.example.com). Es können mehrere Ursprünge verbunden werden, indem diese API mehrmals aufgerufen wird.
Kotlin
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html")
// This initiates a connection to the origin https://www.example.com
}
Java
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html");
// This initiates a connection to the origin https://www.example.com
}
Gängige Konfiguration: PrefetchParameters und PrerenderParameters
Sowohl Prefetch als auch Prerender verwenden PrefetchParameters oder PrerenderParameters, um die Anfrage anzupassen. Mit diesen Klassen können Sie zusätzliche Header und Hinweise für den URL-Abgleich bereitstellen, z. B. No-Vary-Search-Konfigurationen.
Kotlin
// Isolated configuration specifically for Cache-Level Prefetching
val prefetchParams = PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Client", "Android-App-V2")
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, listOf("session_id", "click_ref"))
)
.build()
Java
PrefetchParameters prefetchParams = new PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Header", "value")
/**
* Hint to ignore specific query parameters during cache matching.
* This allows the cache to match even if the tracking_id differs.
*/
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, Arrays.asList("tracking_id"))
)
/**
* Determines if Client Hints are sent.
* NOTE: This is ignored for Prerendering API requests, which default to
* the WebView's WebSettings.getJavaScriptEnabled() value.
*/
.setJavaScriptEnabled(true)
.build();
Inhalte vorab abrufen
Beim Prefetching wird die primäre HTML-Ressource einer URL heruntergeladen und im Netzwerkcache des Profils gespeichert. In WebView fungiert ein Profile als Container für Browserdaten, einschließlich Cookies, HTTP-Cache und Service Workern. Da das Prefetching ein Vorgang auf Profilebene ist, kann jede WebView, die mit diesem Profil verknüpft ist, die im Cache gespeicherte Antwort nutzen.
Implementierung
Rufen Sie prefetchUrlAsync() für eine Profile-Instanz auf, um einen Prefetch zu starten. Diese Operation unterstützt nur das HTTPS-Schema.
Kotlin
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
object : WebViewOutcomeReceiver<PrefetchResult, PrefetchException> {
override fun onResult(result: PrefetchResult) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
override fun onError(error: PrefetchException) {
when (error) {
is PrefetchNetworkException -> {
// Isolates network layer or server-side HTTP anomalies
val code = error.httpStatusCode
// Facilitates rapid diagnosis of 4xx or 5xx server responses
}
else -> {
// Catches generalized execution failures and system constraints
}
}
}
}
)
Java
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
new WebViewOutcomeReceiver<PrefetchResult, PrefetchException>() {
@Override
public void onResult(PrefetchResult result) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
@Override
public void onError(PrefetchException error) {
if (error instanceof PrefetchNetworkException) {
// Isolates network layer or server-side HTTP anomalies
int code = ((PrefetchNetworkException) error).httpStatusCode;
// Facilitates rapid diagnosis of 4xx or 5xx server responses
} else {
// Catches generalized execution failures and system constraints
}
}
}
);
Lebenszyklus von Abfangvorgängen
Durch die WebView-Prefetch-Anfrage wird geändert, wann und wie der shouldInterceptRequest()-Callback ausgelöst wird. Da dies einen direkten Einfluss darauf hat, ob Ihre vorab abgerufenen Inhalte erfolgreich verwendet werden, ist es wichtig, den zweistufigen Lebenszyklus zu verstehen:
1. Die spekulative Phase (Prefetch-Anfrage)
Wenn prefetchUrlAsync() aufgerufen wird, lädt WebView die HTML-Hauptressource im Hintergrund herunter. shouldInterceptRequest() wird für diese Hintergrundanfrage vollständig übersprungen. Benutzerdefinierte Logik, Autorisierungstokens oder Header-Injections, die normalerweise in Ihrem Interceptor verarbeitet werden, werden nicht auf die vorab abgerufene HTML-Ressource angewendet.
2. Die Navigationsphase (Nutzeraktivierung)
Wenn die App explizit zur URL navigiert (z. B. mit loadUrl()) oder der Nutzer auf einen passenden Link klickt, ermittelt WebView, ob der vorab abgerufene Cache verwendet werden kann:
Haupt-HTML-Auswertung:WebView löst
shouldInterceptRequest()für das Haupt-HTML aus. Damit die Seite erfolgreich aus dem Prefetch-Cache bereitgestellt werden kann, muss Ihr Interceptornullzurückgeben. Wenn Sie einen benutzerdefiniertenWebResourceResponsezurückgeben, berücksichtigt WebView Ihren Interceptor und umgeht den Prefetch-Cache vollständig.Unterressourcenauswertung:Nachdem das vorab abgerufene HTML für die Verwendung freigegeben wurde, wird
shouldInterceptRequest()normalerweise für alle nachfolgenden Unterressourcen (z. B. Bilder, Skripts und CSS) ausgelöst, die zum Rendern der Seite erforderlich sind.
Wichtige Verhaltensweisen
Die folgenden Betriebsmerkmale und Eignungsprüfungen regeln, wie WebView Prefetch-Anfragen initiiert und verwaltet:
- Threadsicherheit:Anfragen können von jedem Thread aus initiiert werden.
- Voraussetzungen:Bevor ein Abruf gestartet wird, prüft WebView, ob die Anfrage sicher und kontextbezogen angemessen ist. Dazu werden die folgenden Punkte geprüft:
- Vorhandene Cookies:Zum Schutz der Privatsphäre von Nutzern und zur Vermeidung von CSRF-ähnlichen Nebeneffekten kann WebView das Vorabrufen überspringen, wenn für die Anfrage bestimmte authentifizierte Cookies erforderlich sind, die eine Zustandsänderung auf dem Server auslösen könnten.
- ServiceWorker vorhanden:Wenn ein ServiceWorker bereits den Bereich der URL steuert, kann WebView den Fetch-Handler des ServiceWorkers verwenden, anstatt ein Standard-Netzwerk-Prefetch zu starten.
- Proxy-Verfügbarkeit:WebView prüft, ob der aktuelle Netzwerkpfad (einschließlich aller konfigurierten Proxys) stabil ist, um zu vermeiden, dass spekulative Anfragen bei komplexen Netzwerkkonfigurationen fehlschlagen.
- Wenn ein Prefetch nicht gestartet wird (auch nicht mit gültigen Parametern), liegt das häufig daran, dass WebView festgestellt hat, dass eine Hintergrundanfrage die aktuelle Sitzung oder den Sicherheitsstatus des Nutzers beeinträchtigen könnte.
- Abbrechen:Verwenden Sie
CancellationSignal, um eine laufende Anfrage zu beenden und zu verhindern, dass sie im Cache gespeichert wird.
Seiten vorab rendern
Beim Prerendering werden verborgene „Webinhalte“ erstellt, um eine Seite vollständig im Hintergrund zu rendern, einschließlich der Ausführung von Skripts und des Abrufs von untergeordneten Ressourcen. Das Vorrendern basiert auf derselben zugrunde liegenden Infrastruktur wie das Prefetching. Wenn eine App einen Prerender-Vorgang initiiert, ruft WebView zuerst die Antwort für die Prerender-Navigation ab, um redundante Netzwerkaktivitäten zu vermeiden.
Implementierung
Das Vorrendern ist ein Vorgang auf WebView-Instanzebene. Rufen Sie prerenderUrlAsync() mit WebViewCompat aus dem UI-Thread auf.
Kotlin
WebViewCompat.prerenderUrlAsync(
webView,
url,
cancellationSignal,
executor,
params,
object : PrerenderOperationCallback {
override fun onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in
}
override fun onError(exception: Throwable) {
// exception is an instance of PrerenderException
// Handle prerender failure (for example, memory pressure or disallowed JavaScript APIs)
}
}
)
Java
WebViewCompat.prerenderUrlAsync(webView, url, cancellationSignal, executor, params, new PrerenderOperationCallback() {
@Override
public void onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in.
}
@Override
public void onError(@NonNull Throwable exception) {
// Handle prerender failure (for example, resource constraints or disallowed APIs).
}
});
Sowohl Prefetch als auch Prerender sind vollständig asynchron. prefetchUrlAsync() kann von jedem Thread aus aufgerufen werden, während prerenderUrlAsync() vom UI-Thread aus initiiert werden muss.
Technische Einschränkungen
Um die sofortige Navigation mit der Systemintegrität in Einklang zu bringen, erzwingt WebView die folgenden Laufzeitbeschränkungen:
- Speicherbelastung:WebView bricht vorgerenderte URLs ab, wenn auf dem Gerät wenig RAM verfügbar ist.
- Nicht zulässige APIs:Wenn JavaScript versucht, in einem Hintergrundkontext auf bestimmte APIs zuzugreifen (z. B. für die Audiowiedergabe oder Benachrichtigungen), wird das Prerendering sofort beendet.
- Instanzlimit:Es gibt ein Limit für die Anzahl der aktiven vorgerenderten URLs, die pro WebView zulässig sind.
URL-Abgleich und No-Vary-Search (NVS)
WebView benötigt einen zuverlässigen Abgleichsalgorithmus, um sicherzustellen, dass eine vorab geladene Ressource nur für die beabsichtigte Navigation bereitgestellt wird.
Genaue Übereinstimmung im Vergleich zur NVS-Übereinstimmung
Standardmäßig erfordern Prefetch und Prerender eine genaue URL-Übereinstimmung. Wenn die aufgerufene URL mit der vorab geladenen URL identisch ist, wird sie sofort aus dem Cache bereitgestellt. Wenn sich die Suchparameter unterscheiden, verwendet WebView die folgenden NVS-Regeln (No-Vary-Search):
- Der Hinweis:Entwickler geben bei der Initiierung einen
setExpectedNoVarySearchHeader()-Hinweis an. Wenn die aufgerufene URL mit der Anfrage-URL abzüglich der angegebenen Parameter übereinstimmt, wird WebView kurz blockiert, um auf die tatsächlichen Header des Servers zu warten. - Serverheader:Der NVS-Antwortheader vom Server ist die maßgebliche Quelle. Wenn der Server bestätigt, dass Abfrageunterschiede ignoriert werden sollen, wird die Übereinstimmung aus dem Cache bereitgestellt. Andernfalls wird in WebView ein Kaltstart des Netzwerks ausgeführt.
No-Vary-Search (NVS) ist für die erweiterte Nutzung vorgesehen und die meisten Entwickler benötigen es möglicherweise nicht, da sie sowohl für Prefetch als auch für loadUrl() dieselbe URL übergeben. Diese Anleitung ist nur erforderlich, wenn sich die Suchparameter der Prefetch-URL und der loadUrl()-URL unterscheiden.
Globale Konfiguration
Sie können das spekulative Ladeverhalten auf Profilebene anpassen, indem Sie PrefetchCache-Grenzwerte und die maximale Anzahl von Vorrenderings konfigurieren. Sie können benutzerdefinierte Vorabruf-Grenzwerte auch auf die Systemstandardwerte zurücksetzen:
Kotlin
// Configure prefetch cache limits
profile.prefetchCache.setMaxPrefetches(10)
profile.prefetchCache.setPrefetchTtlSeconds(60)
// Reset to system defaults when needed
profile.prefetchCache.clearMaxPrefetches()
// Configure maximum active prerenders
profile.setMaxPrerenders(2)
Java
// Configure prefetch cache limits
PrefetchCache prefetchCache = profile.getPrefetchCache();
prefetchCache.setMaxPrefetches(10);
prefetchCache.setPrefetchTtlSeconds(60);
// Reset to system defaults when needed
prefetchCache.clearMaxPrefetches();
// Configure maximum active prerenders
profile.setMaxPrerenders(2);
Fehlerbehandlung und Ausnahmen
Bei spekulativen Vorgängen werden Ergebnisse mit OutcomeReceiverCompat oder PrerenderOperationCallback gemeldet.
Primäre Ausnahmen
Wenn ein spekulatives Laden fehlschlägt, meldet Ihr Fehler-Handler einen der folgenden primären Ausnahmetypen, um Ihnen bei der Diagnose bestimmter Fehlerszenarien zu helfen:
PrefetchException:Die Basisklasse für alle asynchronen Prefetch-Fehler.PrefetchNetworkException:Weist auf einen Fehler auf Netzwerk- oder Serverebene hin. Es kann einhttpStatusCode-Feld (z. B. 404 oder 503) enthalten, um serverseitige Probleme zu diagnostizieren.PrerenderException:Die Basisklasse für alle Vorrenderungsfehler, z. B. Fehler aufgrund von Speichermangel oder der Verwendung nicht zulässiger APIs (z. B. Audiowiedergabe) im Hintergrund.
Optimierungsstrategien
Mit diesen Empfehlungen können Sie die Vorteile des spekulativen Ladens maximieren und gleichzeitig Systemressourcen schonen:
- Frühzeitig initiieren:Beginnen Sie mit dem Prefetching beim Start der App oder sobald ein Navigationsziel wahrscheinlich ist.
- Integrierte Strategie:Wenn Sie eine URL, die sich bereits im Prefetch-Cache befindet, vorrendern, wird die Vorrender-Navigation aus diesem Cache bereitgestellt. So werden redundante Netzwerkanfragen vermieden.
- Kontingente im Blick behalten:Das Prerendering ist ressourcenintensiv. Verwenden Sie Prefetching für mehrere wahrscheinliche Kandidaten und reservieren Sie Prerendering für die wahrscheinlichste Navigation.
- Unterstützung von Schemas:Alle URLs müssen das obligatorische HTTPS-Schema verwenden. Ungültige Schemas oder Nulleingaben lösen ein synchrones
IllegalArgumentExceptionaus.
Zusätzliche Ressourcen
Weitere Informationen zum Debuggen von Web-Apps, zum Optimieren der WebView-Startleistung und zum Behandeln des Beendens von Renderer-Prozessen finden Sie in den folgenden Ressourcen:
- Fehler bei Web-Apps beheben
- WebView-Start optimieren
- Beenden des WebView-Rendererprozesses verarbeiten