Simplifica la implementación de WebView con Jetpack Webkit

En esta guía, se describen los beneficios de la biblioteca de Jetpack Webkit, se explica cómo funciona y cómo puedes implementarla en tus proyectos.

Descripción general

Los objetos WebView son una parte esencial del desarrollo de Android, pero a veces pueden ser difíciles de administrar debido a las incoherencias en las funciones en las diferentes versiones del SO Android. Cada versión del SO Android proporciona un conjunto fijo de APIs de WebView. Como Android se envía a un ritmo más lento que WebView, es posible que las APIs de Android no cubran todas las funciones de WebView disponibles. Esto genera una implementación de funciones más lenta y un aumento de los costos de prueba.

Jetpack Webkit resuelve estos problemas actuando como una capa de compatibilidad y aprovechando el APK de WebView actualizado en el dispositivo del usuario. También contiene APIs nuevas y modernas que están disponibles exclusivamente en esta biblioteca.

¿Por qué usar Jetpack Webkit?

Además de ofrecer compatibilidad entre versiones, Jetpack Webkit también ofrece APIs nuevas y modernas que pueden simplificar el desarrollo y mejorar la funcionalidad de tu app:

  • Permite la autenticación moderna: WebView puede controlar sin problemas los estándares de autenticación web modernos, como WebAuthn, lo que permite el acceso basado en llaves de acceso. La biblioteca androidx.webkit te brinda control total sobre esta integración con el método WebSettingsCompat.setWebAuthenticationSupport, que puedes usar para configurar el nivel de compatibilidad que requiere tu app.

  • Mejora el rendimiento: Ajusta el rendimiento de WebView con APIs como setBackForwardCacheEnabled o reduce la latencia de navegación con APIs de carga especulativa, como prefetchUrlAsync y prerenderUrlAsync. Para obtener más información, consulta Carga especulativa en WebView.

  • Aumenta la estabilidad: Recupera los procesos de renderizador bloqueados o que no responden sin fallar. Para obtener más información, consulta WebViewRenderProcess#terminate.

  • Ofrece un control detallado sobre los datos de navegación: Para borrar los datos de navegación almacenados por WebView para orígenes específicos, usa la clase WebStorageCompat.

  • Proporciona una navegación de página mejorada: Reemplaza WebView.loadUrl por WebViewCompat.navigate para obtener un control de navegación detallado, reemplazo de entradas de historial , compatibilidad con encabezados de estado guardado y seguimiento del ciclo de vida correlacionado con NavigationListener. Para obtener más información, consulta Navegación de página mejorada con WebViewCompat.navigate.

  • Optimiza la administración de estados: Protégete contra TransactionTooLargeException estableciendo límites máximos de bytes durante la serialización de estados. Para obtener más información, consulta Administra el estado de WebView de manera eficiente.

Comprende los componentes

Para usar Jetpack Webkit de manera eficaz, debes comprender la relación entre los siguientes componentes:

  • WebView del sistema Android: Es el motor de renderización basado en Chromium que Google actualiza con regularidad a través de Google Play Store con el mismo ritmo que Chrome. Contiene las funciones más actualizadas y proporciona el código de implementación subyacente para todas las APIs de WebView.

  • APIs de framework (android.webkit): Son las APIs que se fijan a una versión específica del SO Android. Por ejemplo, una app en Android 10 solo puede acceder a las APIs que estaban disponibles cuando se lanzó esa versión. Por lo tanto, no puede usar las funciones nuevas que se agregaron al APK de WebView en actualizaciones más recientes. Por ejemplo, para controlar un renderizador que no responde con WebView#getWebViewRenderProcess(), solo puedes llamar a este en Android 10 y versiones posteriores.

  • Biblioteca de Jetpack Webkit (androidx.webkit): Es una biblioteca pequeña incluida en tu aplicación. Esta biblioteca actúa como un puente que llama al APK de WebView, en lugar de llamar a las APIs definidas en la plataforma de Android, que tiene una versión fija del SO. De esta manera, incluso cuando se instala una aplicación en un dispositivo que ejecuta una versión anterior del SO, como Android 10, la aplicación puede usar las funciones más recientes de WebView. Por ejemplo, WebViewCompat.getWebViewRenderProcess() funciona de manera similar a la API de framework, excepto que también se puede llamar en todas las versiones del SO anteriores a Android 10.

Si una API está disponible en el framework y en Jetpack Webkit, te recomendamos que elijas la versión de Jetpack Webkit. Esto ayuda a garantizar un comportamiento y una compatibilidad coherentes en la mayor cantidad de dispositivos.

Interacción entre Jetpack Webkit y APK

Las APIs de Jetpack Webkit se implementan en dos partes:

  • Jetpack Webkit estático: La biblioteca de Jetpack Webkit estática contiene una minoría del código responsable de implementar la API.

  • APK de WebView: El APK de WebView contiene la mayor parte del código.

Tu app llama a la API de Jetpack Webkit, que luego llama al APK de WebView.

Si bien controlas la versión de Jetpack Webkit en tu app, no puedes controlar las actualizaciones del APK de WebView en los dispositivos de los usuarios. Por lo general, la mayoría de los usuarios tienen versiones actualizadas del APK de WebView, pero tu app debe tener cuidado de no llamar a las APIs que no admite esa versión en particular del APK de WebView.

Jetpack Webkit también abstrae la necesidad de verificar las versiones de WebView de forma manual. Para determinar si una función está disponible, verifica su constante de función. Por ejemplo, WebViewFeature.WEB_AUTHENTICATION.

Cómo funcionan en conjunto

Jetpack Webkit une la brecha entre la API de framework estática y el APK de WebView que se actualiza con frecuencia. Cuando usas la API de Jetpack Webkit con el patrón de detección de funciones, la biblioteca realiza una verificación para ver si la función es compatible con el APK de WebView instalado en el dispositivo del usuario. Esto proporciona el beneficio de no tener que verificar la versión del SO Android (framework).

Si el APK de WebView es una versión lo suficientemente reciente, la biblioteca invoca la función. De lo contrario, informa que la función no está disponible, lo que evita que tu app falle y te permite controlar la situación correctamente.

Compara las APIs de Jetpack Webkit y de framework

En esta sección, se comparan los métodos de implementación con y sin la biblioteca de Jetpack Webkit:

Habilita la autenticación moderna (WebAuthn)

Sin Jetpack Webkit

No es posible a través de las APIs de framework.

Con Jetpack Webkit

Aprovecha WebViewFeature.WEB_AUTHENTICATION para las verificaciones de compatibilidad.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) {
  WebSettingsCompat.setWebAuthenticationSupport(
      webView.settings,
      WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP
  )
}

Borra datos de un origen (almacenamiento específico del sitio)

Sin Jetpack Webkit

No hay una API directa para borrar datos de origen específicos. A menudo, se requiere borrar todos los datos.

Con Jetpack Webkit

Usa APIs de compatibilidad para borrar datos precisos. Puedes usar cualquiera de las siguientes opciones:

WebStorageCompat.getInstance().deleteBrowsingData()

O

WebStorageCompat.getInstance().deleteBrowsingDataForSite()

Obtén la versión de WebView

Sin Jetpack Webkit

Usa la clase de framework estándar.

val webViewPackage = WebView.getCurrentWebViewPackage()

Con Jetpack Webkit

Usa la capa de compatibilidad para una recuperación más segura.

val webViewPackage = WebViewCompat.getCurrentWebViewPackage()

Controla el renderizador que no responde (cliente del renderizador)

Sin Jetpack Webkit

Usa el método de framework estándar.

webView.setWebViewRenderProcessClient(myClient)

Con Jetpack Webkit

Usa WebViewCompat y una verificación de funciones para configurar el cliente.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) {
  WebViewCompat.setWebViewRenderProcessClient(webView, myClient)
}

Para obtener orientación sobre la implementación de estrategias de recuperación ante fallas, consulta Controla la finalización de WebView termination. Para obtener detalles de la API, consulta la androidx.webkit documentación de referencia.

Administra el estado guardado y el tamaño de la transacción

La API de WebViewCompat.saveState te permite aplicar límites de bytes y reducir el historial de avance durante la serialización, lo que evita fallas de TransactionTooLargeException y conserva el historial de navegación esencial.

Sin Jetpack Webkit

Usa el método de framework estándar, que serializa toda la pila de navegación sin límites de tamaño y puede activar TransactionTooLargeException si la carga útil excede el límite de transacción de 1 MB de Android.

webView.saveState(outState)

Con Jetpack Webkit

Usa WebViewCompat para aplicar un límite máximo de bytes o descartar entradas de navegación hacia adelante, lo que protege contra los desbordamientos de transacciones.

WebViewCompat.saveState(webView, outState, maxSizeBytes)

Para obtener más información, consulta Administra el estado de WebView de manera eficiente.

Para navegar por páginas web con compatibilidad para el reemplazo de entradas de historial, compatibilidad con encabezados de estado guardado y devoluciones de llamada de ciclo de vida correlacionadas, usa WebViewCompat.navigate en lugar de WebView.loadUrl.

Sin Jetpack Webkit

Usa WebView.loadUrl, que no admite el reemplazo de entradas de historial ni el seguimiento de devoluciones de llamada de ciclo de vida correlacionadas.

webView.loadUrl("https://www.example.com")

Con Jetpack Webkit

Usa WebViewCompat.navigate con NavigationParameters para reemplazar entradas de historial, conservar encabezados personalizados en el estado guardado y hacer un seguimiento de los estados de navegación.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
  val params = NavigationParameters.Builder()
      .setShouldReplaceCurrentEntry(true)
      .build()
  val navigation = WebViewCompat.navigate(webView, "https://www.example.com", params)
} else {
  webView.loadUrl("https://www.example.com")
}

Para obtener más información sobre el seguimiento de la navegación y la configuración de parámetros, consulta Navegación de página mejorada con WebViewCompat.navigate.

Integra Jetpack Webkit en tu código

El uso de Jetpack Webkit aumenta las capacidades de la clase WebView estándar, pero no reemplaza por completo la clase WebView original.

Puedes seguir usando la android.webkit.WebView clase. Puedes agregarla a tus diseños XML y obtener una referencia a la instancia en tu código. Para acceder a las funciones estándar del framework, puedes llamar a los métodos directamente en la instancia de WebView o en su objeto de configuración.

Para acceder a las funciones modernas, usa los métodos auxiliares estáticos que proporciona Jetpack Webkit, como WebViewCompat y WebSettingsCompat. Pasa tu instancia de WebView existente a estos métodos.

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

// You still get your WebView instance the standard way.
val webView: WebView = findViewById(R.id.my_webview)

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

// You still get your WebView instance the standard way.
WebView webView = findViewById(R.id.my_webview);

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON);
}

Implementa Jetpack Webkit

Para implementar Jetpack Webkit, usa el siguiente procedimiento.

Paso 1: Agrega la dependencia

En el archivo build.gradle.kts o build.gradle de tu módulo, incluye la siguiente dependencia para agregar Jetpack Webkit:

Groovy

dependencies {
    implementation "androidx.webkit:webkit:1.16.0"
}

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.16.0")
}

Jetpack Webkit contiene wrappers delgados, por lo que el impacto en el tamaño de tu aplicación es mínimo.

Paso 2: Adopta el patrón de detección de funciones

Para evitar fallas cuando se invocan APIs no disponibles, usa verificaciones de funciones. Te recomendamos que rodees cada invocación de API con una verificación de funciones y que consideres la lógica de resguardo para cuando la API no esté disponible.

Recomendamos el siguiente patrón para usar una API de WebView moderna:

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

val webView: WebView = findViewById(R.id.my_webview)

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
} else {
    // Optionally, provide a fallback for older WebView versions.
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

WebView webView = findViewById(R.id.my_webview);

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else {
    // Optionally, provide a fallback for older WebView versions.
}

Este patrón ayuda a garantizar que la aplicación sea sólida. Como la verificación de funciones se ejecuta primero, la aplicación no falla si la función no está disponible. La sobrecarga de rendimiento de la WebViewFeature#isFeatureSupported verificación es insignificante.