WebViewCompat.navigate est une alternative améliorée à
WebView.loadUrl qui offre un contrôle précis sur le chargement des pages Web,
la gestion de l'historique et le suivi du cycle de vie de la navigation dans WebView.
Auparavant, le lancement de la navigation sur une page à l'aide de loadUrl présentait des limites notables :
- Aucun remplacement d'entrée d'historique : vous ne pouviez pas remplacer l'entrée d'historique actuelle, ce qui rendait impossible la navigation vers une nouvelle page sans ajouter d'entrée à la pile "Retour".
- Rappels découplés : il n'existait aucun mécanisme direct pour corréler un appel
loadUrlspécifique avec les événements de rappel ultérieurs dansWebViewClient. - En-têtes supplémentaires non enregistrés : les en-têtes personnalisés transmis à
loadUrln'étaient pas enregistrés dans l'étatWebView. Ils étaient donc perdus lors de la restauration de l'état.
L'API WebViewCompat.navigate résout ces problèmes en introduisant les fonctionnalités suivantes :
- Remplacement d'entrée d'historique de navigation : vous permet de remplacer la page actuelle dans la pile d'historique
WebView. - Suivi des rappels corrélés : renvoie un objet
Navigationqui sert d'identifiant unique à toutes les étapes d'un cycle de vie de navigation. - Prise en charge des en-têtes d'état enregistrés : les en-têtes supplémentaires sont enregistrés de manière fiable dans le bundle d'état
WebView, ce qui leur permet d'être réutilisés lors de la restauration de l'état.
Fonctionnalités et limites clés
Avant d'adopter WebViewCompat.navigate, tenez compte des règles et contraintes opérationnelles suivantes :
Sécurité des threads : vous devez appeler
WebViewCompat.navigatesur le thread d'interface utilisateur (principal).Annulation et priorité : les navigations en cours ne peuvent pas être annulées explicitement. Toutefois, le lancement d'un nouvel appel
navigatesur le mêmeWebViewremplace toute navigation active.Prise en charge des schémas d'URI : les schémas d'URI standards (tels que
https:ethttp:) et personnalisés sont compatibles. Le schémajavascript:n'est pas compatible.Limite de taille des URL : la longueur maximale de la chaîne d'URL acceptée est de 2 Mo.
Vérification des fonctionnalités : vérifiez toujours la disponibilité des fonctionnalités à l'aide de
WebViewFeature.isFeatureSupportedavant d'appeler l'API pour maintenir la compatibilité entre les différentes versions d'APK WebView.
Lancer la navigation et suivre le cycle de vie
Pour configurer la navigation et suivre son cycle de vie, procédez comme suit :
- Enregistrez une
NavigationListenerimplémentation à l'aide deWebViewCompat.addNavigationListenerlors de la configurationWebViewpour recevoir des rappels de cycle de vie structurés. Enregistrez l'écouteur une seule fois (plutôt qu'à chaque appel de navigation) pour éviter les fuites de mémoire et les exécutions de rappels en double. - Créez une instance
NavigationParametersà l'aide deNavigationParameters.Builderpour spécifier des comportements facultatifs, tels que le remplacement de l'historique ou des en-têtes HTTP personnalisés. - Appelez
WebViewCompat.navigate, en transmettant votre instanceWebView, l' URL de destination et les paramètres.
WebViewCompat.navigate renvoie un Navigation objet qui identifie de manière unique
la requête. Dans vos NavigationListener rappels, comparez
cet objet avec le paramètre Navigation entrant pour suivre cette navigation spécifique.
Exemple de mise en œuvre
L'exemple suivant montre comment configurer les paramètres de navigation, appeler WebViewCompat.navigate et écouter les événements du cycle de vie de la navigation :
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);
}
}
Modes de défaillance et gestion des erreurs
L'API WebViewCompat.navigate fournit des mécanismes distincts pour gérer les erreurs de configuration et les échecs de navigation lors de l'exécution :
Exceptions d'arguments non valides
La transmission d'arguments non valides déclenche une IllegalArgumentException synchrone.
Les causes les plus courantes sont les suivantes :
- Transmission de
nullpour les paramètres obligatoires non nuls (webView,urlouparams). - Fourniture d'un schéma d'URL non compatible, tel que
javascript:. - Transmission de clés ou de valeurs d'en-tête HTTP mal formées qui ne sont pas conformes aux spécifications RFC 2616.
Erreurs de processus de navigation
En cas d'échec lors de la requête réseau ou du chargement de page (par exemple, un code d'état HTTP 404
état, un échec de résolution DNS ou une erreur SSL), WebViewCompat.navigate
renvoie toujours un objet Navigation valide.
Une fois la navigation terminée, examinez les méthodes suivantes sur l'Navigation
instance dans votre onNavigationCompleted rappel pour diagnostiquer l'
échec :
getStatusCode: renvoie le code d'état de la réponse HTTP (par exemple,404ou500).getWebResourceError: renvoie un objetWebResourceErrorCompatdétaillant les erreurs réseau, telles que les délais d'inactivité de connexion ou les échecs de recherche d'hôte.didCommitErrorPage: indique siWebViewa validé et affiché une page d'erreur à l'utilisateur.didCommit: indique si la navigation a été validée avec succès dans une page cible sans être interrompue.
Gestion des bundles d'état enregistrés
Lorsque vous transmettez des en-têtes supplémentaires avec NavigationParameters, WebView les enregistre dans son bundle d'état enregistré afin qu'ils puissent être réutilisés lors de la restauration de l'état. Toutefois, les grandes collections d'en-têtes peuvent augmenter considérablement la taille du Bundle d'état enregistré.
Si vous devez limiter la taille du bundle pour éviter TransactionTooLargeException
lors des enregistrements d'état Android, utilisez WebViewCompat.saveState. Cette méthode vous permet de définir une limite de taille maximale du bundle en octets et d'exclure éventuellement les éléments d'historique avant :
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);
Le bundle résultant reste compatible avec la méthode standard
WebView.restoreState.
Recommandations de migration et de mise en œuvre
Pour garantir des performances et une stabilité optimales lors de la navigation dans WebView, suivez ces recommandations :
Migrer de
loadUrlversnavigate: migrez tous les appels héritésWebView.loadUrlversWebViewCompat.navigate. Cela garantit une gestion uniforme de l'historique et que les en-têtes sont toujours enregistrés dans l'état enregistré.Vérifier toujours la prise en charge des fonctionnalités : avant d'appeler l'API, vérifiez la prise en charge de l'exécution avec
WebViewFeature.isFeatureSupportedpour vous protéger contre les anciennes versions de WebView.Corréler les instances de navigation : utilisez l'objet
Navigationrenvoyé pour différencier les navigations simultanées ou filtrer les rappels lorsque vous gérez plusieurs instancesWebView.Enregistrer l'écouteur une seule fois lors de l'initialisation : étant donné que
WebViewCompat.addNavigationListenerajoute un écouteur au lieu d'en remplacer un existant, enregistrez votreNavigationListenerune seule fois lors de la configuration deWebViewpour éviter les fuites de mémoire et les exécutions de rappels en double lors des navigations ultérieures.Surveiller la taille de l'état enregistré : lorsque vous transmettez des charges utiles d'en-tête volumineuses, utilisez
WebViewCompat.saveStateavec des limites de taille explicites pour éviter d'enregistrer des données d'état excessives.
Ressources supplémentaires
Pour en savoir plus sur les fonctionnalités Web intégrées et l'optimisation des performances, consultez les guides suivants :
- Simplifier l'implémentation de WebView avec Jetpack Webkit
- Chargement spéculatif dans WebView
- Optimiser le démarrage de WebView
- Gérer l'arrêt du processus de rendu WebView