WebViewCompat.navigate es una alternativa mejorada a
WebView.loadUrl que proporciona un control detallado sobre la carga de páginas web,
la administración del historial y el seguimiento del ciclo de vida de navegación en WebView.
Anteriormente, iniciar la navegación de páginas con loadUrl tenía limitaciones notables:
- No se reemplaza la entrada del historial: No podías reemplazar la entrada del historial actual, lo que imposibilitaba navegar a una página nueva sin agregar una entrada a la pila de actividades.
- Devoluciones de llamada desacopladas: No había un mecanismo directo para correlacionar una llamada
loadUrlespecífica con eventos de devolución de llamada posteriores enWebViewClient. - No se guardan los encabezados adicionales: Los encabezados personalizados que se pasan a
loadUrlno se guardan como parte del estadoWebView, por lo que se perdían cuando se restablecía el estado.
La API de WebViewCompat.navigate resuelve estos problemas con la introducción de las siguientes funciones:
- Reemplazo de entrada del historial de navegación: Te permite reemplazar la página actual en la pila del historial de
WebView. - Seguimiento de devoluciones de llamada correlacionadas: Muestra un objeto
Navigationque funciona como un identificador único en todas las etapas de un ciclo de vida de navegación. - Compatibilidad con encabezados de estado guardado: Los encabezados adicionales se guardan de forma confiable en el paquete de estado
WebViewpara que se puedan reutilizar cuando se restablezca el estado.
Funcionalidades y limitaciones clave
Antes de adoptar WebViewCompat.navigate, considera las siguientes reglas y restricciones operativas:
Seguridad de subprocesos: Debes invocar
WebViewCompat.navigateen el subproceso de IU (principal).Cancelación y precedencia: Las navegaciones en curso no se pueden cancelar de forma explícita. Sin embargo, iniciar una nueva llamada
navigateen el mismoWebViewreemplaza cualquier navegación activa.Compatibilidad con esquemas de URI: Se admiten esquemas de URI estándar (como
https:yhttp:) y personalizados. No se admite el esquemajavascript:.Límite de tamaño de URL: La longitud máxima de cadena de URL admitida es de 2 MB.
Verificación de funciones: Siempre verifica la disponibilidad de funciones con
WebViewFeature.isFeatureSupportedantes de invocar la API para mantener la compatibilidad en diferentes versiones de APK de WebView.
Inicia la navegación y haz un seguimiento del ciclo de vida
Para configurar la navegación y hacer un seguimiento de su ciclo de vida, haz lo siguiente:
- Registra una
NavigationListenerimplementación conWebViewCompat.addNavigationListenerduranteWebViewconfiguración para recibir devoluciones de llamada estructuradas del ciclo de vida. Registra el objeto de escucha una vez (en lugar de en cada llamada de navegación) para evitar pérdidas de memoria y ejecuciones de devolución de llamada duplicadas. - Crea una instancia de
NavigationParametersconNavigationParameters.Builderpara especificar comportamientos opcionales, como el reemplazo del historial o encabezados HTTP personalizados. - Llama a
WebViewCompat.navigatey pasa tu instancia deWebView, la URL de destino y los parámetros.
WebViewCompat.navigate muestra un objeto Navigation que identifica de forma única
la solicitud. En tus NavigationListener devoluciones de llamada, compara
este objeto con el parámetro Navigation entrante para hacer un seguimiento de esa navegación específica.
Ejemplo de implementación
En el siguiente ejemplo, se muestra cómo configurar parámetros de navegación, invocar WebViewCompat.navigate y escuchar eventos del ciclo de vida de navegación:
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);
}
}
Modos de falla y manejo de errores
La API de WebViewCompat.navigate proporciona mecanismos distintos para controlar los errores de configuración y las fallas de navegación en el tiempo de ejecución:
Excepciones de argumentos no válidos
Si pasas argumentos no válidos, se activa una IllegalArgumentException síncrona.
Las causas comunes incluyen las siguientes:
- Pasar
nullpara parámetros obligatorios no nulos (webView,urloparams). - Proporcionar un esquema de URL no admitido, como
javascript:. - Pasar claves o valores de encabezado HTTP con formato incorrecto que no cumplen con las especificaciones de RFC 2616.
Errores del proceso de navegación
Si se produce una falla durante la solicitud de red o la carga de la página (como un código de estado HTTP 404
código de estado, una falla de resolución de DNS o un error de SSL), WebViewCompat.navigate
aún muestra un objeto Navigation válido.
Cuando finalice la navegación, inspecciona los siguientes métodos en la Navigation
instancia dentro de tu onNavigationCompleted devolución de llamada para diagnosticar la
falla:
getStatusCode: Muestra el código de estado de respuesta HTTP (por ejemplo,404o500).getWebResourceError: Muestra un objetoWebResourceErrorCompatque detalla los errores de red, como los tiempos de espera de conexión o las fallas de búsqueda de host.didCommitErrorPage: Indica siWebViewconfirmó y mostró una página de error al usuario.didCommit: Indica si la navegación se confirmó correctamente en una página de destino sin que se anule.
Administración de paquetes de estado guardado
Cuando pasas encabezados adicionales con NavigationParameters, WebView guarda estos encabezados en su paquete de estado guardado para que se puedan reutilizar cuando se restablezca el estado. Sin embargo, las colecciones grandes de encabezados pueden aumentar considerablemente el tamaño del Bundle de estado guardado.
Si necesitas restringir el tamaño del paquete para evitar TransactionTooLargeException
durante los guardados de estado de Android, usa WebViewCompat.saveState. Este método te permite establecer un límite máximo de tamaño de paquete en bytes y, de manera opcional, excluir elementos del historial de avance:
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);
El paquete resultante sigue siendo compatible con el método estándar
WebView.restoreState.
Recomendaciones de migración e implementación
Para garantizar un rendimiento y una estabilidad óptimos cuando navegas en WebView, sigue estas recomendaciones:
Migra de
loadUrlanavigate: Migra todas las llamadas heredadasWebView.loadUrlaWebViewCompat.navigate. Esto garantiza una administración uniforme del historial y que los encabezados siempre se guarden como parte del estado guardado.Siempre verifica la compatibilidad con funciones: Antes de invocar la API, confirma la compatibilidad con el tiempo de ejecución con
WebViewFeature.isFeatureSupportedpara protegerte de versiones anteriores de WebView.Correlaciona instancias de navegación: Usa el objeto
Navigationque se muestra para diferenciar las navegaciones simultáneas o filtrar las devoluciones de llamada cuando administras varias instancias deWebView.Registra el objeto de escucha una vez durante la inicialización: Debido a que
WebViewCompat.addNavigationListeneragrega un objeto de escucha en lugar de reemplazar uno existente, registra tuNavigationListeneruna vez duranteWebViewla configuración para evitar pérdidas de memoria y ejecuciones de devolución de llamada duplicadas en navegaciones posteriores.Supervisa el tamaño del estado guardado: Cuando pases cargas útiles de encabezado grandes, usa
WebViewCompat.saveStatecon límites de tamaño explícitos para evitar guardar datos de estado excesivos.
Recursos adicionales
Para obtener más información sobre las capacidades web incorporadas y la optimización del rendimiento, consulta las siguientes guías:
- Simplifica tu implementación de WebView con Jetpack Webkit
- Carga especulativa en WebView
- Optimiza el inicio de WebView
- Controla la finalización del proceso de renderizador de WebView