WebViewCompat.navigate è un'alternativa avanzata a
WebView.loadUrl che fornisce un controllo granulare sul caricamento delle pagine web,
sulla gestione della cronologia e sul monitoraggio del ciclo di vita della navigazione in WebView.
In precedenza, l'avvio delle navigazioni nelle pagine utilizzando loadUrl presentava notevoli limitazioni:
- Nessuna sostituzione della voce della cronologia: non era possibile sostituire la voce della cronologia corrente, il che rendeva impossibile passare a una nuova pagina senza aggiungere una voce al back stack.
- Callback disaccoppiati: non esisteva un meccanismo diretto per correlare una chiamata
loadUrlspecifica con gli eventi di callback successivi inWebViewClient. - Intestazioni aggiuntive non salvate: le intestazioni personalizzate passate a
loadUrlnon venivano salvate come parte dello statoWebView, quindi andavano perse durante il ripristino dello stato.
L'API WebViewCompat.navigate risolve questi problemi introducendo le seguenti funzionalità:
- Sostituzione della voce della cronologia di navigazione: consente di sostituire la pagina corrente nello stack della cronologia
WebView. - Monitoraggio dei callback correlati: restituisce un oggetto
Navigationche funge da identificatore univoco in tutte le fasi del ciclo di vita della navigazione. - Supporto delle intestazioni dello stato salvato: le intestazioni aggiuntive vengono salvate in modo affidabile nel bundle di stato
WebViewin modo che possano essere riutilizzate al ripristino dello stato.
Funzionalità e limitazioni principali
Prima di adottare WebViewCompat.navigate, tieni presente le seguenti regole e limitazioni operative:
Thread safety: devi richiamare
WebViewCompat.navigatenel thread dell'interfaccia utente (principale).Annullamento e precedenza: le navigazioni in corso non possono essere annullate esplicitamente. Tuttavia, l'avvio di una nuova chiamata
navigatesullo stessoWebViewsostituisce qualsiasi navigazione attiva.Supporto dello schema URI: sono supportati gli schemi URI standard (ad esempio
https:ehttp:) e personalizzati. Lo schemajavascript:non è supportato.Limite di dimensioni dell'URL: la lunghezza massima della stringa URL supportata è di 2 MB.
Controllo delle funzionalità: controlla sempre la disponibilità delle funzionalità utilizzando
WebViewFeature.isFeatureSupportedprima di richiamare l'API per mantenere la compatibilità tra le diverse versioni APK di WebView.
Avviare la navigazione e monitorare il ciclo di vita
Per configurare la navigazione e monitorarne il ciclo di vita:
- Registra un
NavigationListenerimplementazione utilizzandoWebViewCompat.addNavigationListenerduranteWebViewconfigurazione per ricevere callback strutturati del ciclo di vita. Registra il listener una sola volta (anziché a ogni chiamata di navigazione) per evitare perdite di memoria ed esecuzioni di callback duplicate. - Crea un'istanza di
NavigationParametersutilizzandoNavigationParameters.Builderper specificare comportamenti facoltativi, come la sostituzione della cronologia o le intestazioni HTTP personalizzate. - Chiama
WebViewCompat.navigate, passando l'istanzaWebView, l' URL di destinazione e i parametri.
WebViewCompat.navigate restituisce un oggetto Navigation che identifica in modo univoco
la richiesta. Nei callback NavigationListener, confronta
questo oggetto con il parametro Navigation in entrata per monitorare la navigazione specifica.
Esempio di implementazione
L'esempio seguente mostra come configurare i parametri di navigazione, richiamare WebViewCompat.navigate e ascoltare gli eventi del ciclo di vita della navigazione:
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);
}
}
Modalità di errore e gestione degli errori
L'API WebViewCompat.navigate fornisce meccanismi distinti per la gestione degli errori di configurazione e degli errori di navigazione di runtime:
Eccezioni di argomenti non validi
Il passaggio di argomenti non validi attiva un'eccezione IllegalArgumentException sincrona.
Di seguito sono elencate alcune delle possibili cause:
- Passaggio di
nullper i parametri non null obbligatori (webView,urloparams). - Fornitura di uno schema URL non supportato, ad esempio
javascript:. - Passaggio di chiavi o valori di intestazione HTTP non validi che non sono conformi alle specifiche RFC 2616.
Errori del processo di navigazione
Se si verifica un errore durante la richiesta di rete o il caricamento pagina (ad esempio un codice di stato HTTP 404 codice di stato, un errore di risoluzione DNS o un errore SSL), WebViewCompat.navigate restituisce comunque un oggetto Navigation valido.
Al termine della navigazione, esamina i seguenti metodi nell'Navigation
istanza all'interno del onNavigationCompleted callback per diagnosticare l'
errore:
getStatusCode: restituisce il codice di stato della risposta HTTP (ad esempio404o500).getWebResourceError: restituisce un oggettoWebResourceErrorCompatche descrive in dettaglio gli errori di rete, come i timeout di connessione o gli errori di ricerca dell'host.didCommitErrorPage: indica seWebViewha eseguito il commit e visualizzato una pagina di errore per l'utente.didCommit: indica se la navigazione è stata eseguita correttamente in una pagina di destinazione senza essere interrotta.
Gestione del bundle di stato salvato
Quando passi intestazioni aggiuntive con NavigationParameters, WebView salva queste intestazioni nel bundle di stato salvato in modo che possano essere riutilizzate al ripristino dello stato. Tuttavia, le raccolte di intestazioni di grandi dimensioni possono aumentare notevolmente le dimensioni del Bundle di stato salvato.
Se devi limitare le dimensioni del bundle per evitare TransactionTooLargeException
durante i salvataggi dello stato di Android, utilizza WebViewCompat.saveState. Questo metodo consente di impostare un limite massimo di dimensioni del bundle in byte e, facoltativamente, di escludere gli elementi della cronologia in avanti:
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);
Il bundle risultante rimane compatibile con il metodo standard
WebView.restoreState.
Suggerimenti per la migrazione e l'implementazione
Per garantire prestazioni e stabilità ottimali durante la navigazione in WebView, segui questi consigli:
Esegui la migrazione da
loadUrlanavigate: esegui la migrazione di tutte le chiamate legacyWebView.loadUrlaWebViewCompat.navigate. In questo modo, la gestione della cronologia è uniforme e le intestazioni vengono sempre salvate come parte dello stato salvato.Verifica sempre il supporto delle funzionalità: prima di richiamare l'API, verifica il supporto di runtime con
WebViewFeature.isFeatureSupportedper proteggerti dalle versioni precedenti di WebView.Correlare le istanze di navigazione: utilizza l'oggetto
Navigationrestituito per distinguere le navigazioni simultanee o filtrare i callback quando gestisci più istanzeWebView.Registra il listener una sola volta durante l'inizializzazione: poiché
WebViewCompat.addNavigationListeneraggiunge un listener anziché sostituire uno esistente, registraNavigationListeneruna sola volta duranteWebViewla configurazione per evitare perdite di memoria ed esecuzioni di callback duplicate nelle navigazioni successive.Monitora le dimensioni dello stato salvato: quando passi payload di intestazione di grandi dimensioni, utilizza
WebViewCompat.saveStatecon limiti di dimensioni espliciti per evitare di salvare dati di stato eccessivi.
Risorse aggiuntive
Per saperne di più sulle funzionalità web incorporate e sull'ottimizzazione del rendimento, consulta le seguenti guide:
- Semplificare l'implementazione di WebView con Jetpack Webkit
- Caricamento speculativo in WebView
- Ottimizzare l'avvio di WebView
- Gestire la terminazione del processo di rendering di WebView