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 della navigazione nelle pagine utilizzando loadUrl presentava limiti notevoli:
- Nessuna sostituzione della voce della cronologia: non è stato possibile sostituire la voce della cronologia corrente, rendendo 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 i successivi eventi di callback inWebViewClient. - Intestazioni aggiuntive non salvate: le intestazioni personalizzate trasmesse a
loadUrlnon sono state salvate come parte dello statoWebView, quindi sono andate 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
nella cronologia
WebView. - Monitoraggio dei callback correlati: restituisce un oggetto
Navigationche funge da identificatore univoco in tutte le fasi del ciclo di vita di una navigazione. - Supporto delle intestazioni dello stato salvato: le intestazioni aggiuntive vengono salvate in modo affidabile nel bundle dello 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.navigatesul thread dell'interfaccia utente (principale).Annullamento e precedenza:le navigazioni in volo non possono essere annullate esplicitamente. Tuttavia, l'avvio di una nuova chiamata
navigatesullo stessoWebViewha la precedenza su qualsiasi navigazione attiva.Supporto dello schema URI:sono supportati schemi URI standard (come
https:ehttp:) e personalizzati. Lo schemajavascript:non è supportato.Limite di dimensioni dell'URL:la lunghezza massima della stringa URL supportata è 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 dell'APK WebView.
Avviare la navigazione e monitorare il ciclo di vita
Per configurare la navigazione e monitorarne il ciclo di vita:
- Registra un'implementazione di
NavigationListenerutilizzandoWebViewCompat.addNavigationListenerdurante la configurazione diWebViewper ricevere callback del ciclo di vita strutturati. Registra il listener una sola volta (anziché a ogni chiamata di navigazione) per evitare perdite di memoria ed esecuzioni duplicate dei callback. - Crea un'istanza
NavigationParametersutilizzandoNavigationParameters.Builderper specificare comportamenti facoltativi, ad esempio 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 questa 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);
}
}
Propagare lo stato dell'app utilizzando le intestazioni HTTP
Le app web spesso richiedono il contesto dell'app per Android host per coordinare la logica di backend o personalizzare i contenuti web. L'aggiunta di parametri di ricerca all'URL per trasmettere queste informazioni può ingombrare gli URL, interferire con la memorizzazione nella cache ed esporre lo stato interno dell'app.
Ti consigliamo invece di trasmettere il contesto dell'app utilizzando intestazioni HTTP personalizzate. Utilizzando
WebViewCompat.navigate e NavigationParameters, puoi inviare in modo sicuro questi
dati al tuo server. Inoltre, WebView conserva queste intestazioni durante il ripristino dello stato, il che garantisce che i contenuti web rimangano coerenti in seguito alle modifiche alla configurazione. Tieni presente che questa persistenza si applica solo quando utilizzi
WebViewCompat.navigate. Se utilizzi WebView.loadUrl, le intestazioni personalizzate non vengono
salvate nel bundle di stato WebView e vengono perse al ripristino.
Casi d'uso comuni
I casi d'uso comuni per il passaggio del contesto dell'app host includono:
- Versione dell'app (
X-App-Version): il passaggio della versione di rilascio dell'app host (ad esempioBuildConfig.VERSION_NAME) aiuta il server di backend a verificare la compatibilità del bridge JavaScript nativo, a controllare le funzionalità o a chiedere agli utenti di aggiornare le app meno recenti. - Piattaforma client (
X-Client-Platform): l'identificazione esplicita dell'ambiente host come Android consente al server di fornire un'interfaccia utente personalizzata per la piattaforma o link allo store di route senza fare affidamento all'analisi della stringaUser-Agent.
Esempio di implementazione
L'esempio seguente mostra come trasmettere la versione dell'applicazione e la piattaforma client a un server web:
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);
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 IllegalArgumentException sincrono.
Le cause comuni includono:
- Passaggio di
nullper i parametri obbligatori non nulli (webView,urloparams). - Fornire uno schema URL non supportato, ad esempio
javascript:. - Trasmissione 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, 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'istanza Navigation all'interno del callback onNavigationCompleted per diagnosticare l'errore:
getStatusCode: restituisce il codice di stato della risposta HTTP (ad esempio,404o500).getWebResourceError: restituisce un oggettoWebResourceErrorCompatche descrive in dettaglio gli errori di rete, come timeout di connessione o errori di ricerca dell'host.didCommitErrorPage: indica seWebViewha eseguito il commit e mostrato una pagina di errore all'utente.didCommit: indica se la navigazione è stata eseguita correttamente in una pagina di destinazione senza essere interrotta.
Gestione dei bundle di stati salvati
Quando passi intestazioni aggiuntive con NavigationParameters, WebView salva
queste intestazioni nel bundle di stati salvati in modo che possano essere riutilizzate al ripristino
dello stato. Tuttavia, le raccolte di intestazioni di grandi dimensioni possono aumentare notevolmente le dimensioni dello stato salvato Bundle.
Se devi limitare le dimensioni del bundle per evitare TransactionTooLargeException
durante i salvataggi dello stato di Android, utilizza WebViewCompat.saveState. Questo metodo
ti consente di impostare un limite massimo di dimensioni del bundle in byte ed escludere facoltativamente gli elementi della cronologia
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.
Consigli per la migrazione e l'implementazione
Per garantire prestazioni e stabilità ottimali durante la navigazione in WebView,
segui questi consigli:
Eseguire 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, conferma il supporto del runtime con
WebViewFeature.isFeatureSupportedper proteggerti dalle versioni precedenti di WebView.Correlare le istanze di navigazione:utilizza l'oggetto
Navigationrestituito per differenziare le navigazioni simultanee o filtrare i callback durante la gestione di più istanzeWebView.Registra il listener una volta durante l'inizializzazione:poiché
WebViewCompat.addNavigationListeneraggiunge un listener anziché sostituirne uno esistente, registraNavigationListeneruna volta durante la configurazione diWebViewper evitare perdite di memoria ed esecuzioni di callback duplicate nelle navigazioni successive.Monitora le dimensioni dello stato di salvataggio:quando passi payload di intestazione di grandi dimensioni, utilizza
WebViewCompat.saveStatecon limiti di dimensione espliciti per evitare di salvare dati di stato eccessivi.
Risorse aggiuntive
Per scoprire 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 l'interruzione del processo di rendering di WebView