Android proporciona varias APIs para ayudarte a administrar los objetos WebView que muestran contenido web en tu app.
En esta página, se describe cómo usar las APIs para trabajar con objetos WebView de manera más efectiva y, de ese modo, mejorar la estabilidad y la seguridad de tu app.
API de versión
A partir de Android 7.0 (nivel de API 24), los usuarios pueden elegir entre varios paquetes diferentes para mostrar contenido web en un objeto WebView.
La biblioteca Jetpack Webkit
incluye el
getCurrentWebViewPackage()
método para obtener información relacionada con el paquete que muestra contenido web
en tu app. Este método es útil en el análisis de errores que ocurren solo
cuando tu app intenta mostrar contenido web usando una implementación de un paquete particular de WebView.
Para usar este método, agrega la lógica que se muestra en el siguiente fragmento de código:
Kotlin
val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext) Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")
Java
PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext); Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);
Servicio de Navegación segura de Google
Para que los usuarios obtengan una experiencia de navegación más segura, WebView
los objetos verifican las URLs mediante
la Navegación segura de Google,
que permite que tu app les muestre una advertencia cuando intentan visitar un
sitio web no seguro.
Si bien el valor predeterminado de EnableSafeBrowsing es verdadero, es posible que en algunos casos quieras habilitar la Navegación segura de manera condicional o bien inhabilitarla. La versión 8.0 (API nivel 26) y las versiones posteriores de Android admiten el uso de
setSafeBrowsingEnabled()
para activar o desactivar la Navegación segura en un objeto WebView individual.
Si deseas que todos los WebView objetos inhabiliten las verificaciones de la Navegación segura, agrega el siguiente <meta-data> elemento al archivo de manifiesto de tu app:
<manifest> <application> <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" /> ... </application> </manifest>
Cómo definir acciones programáticas
Cuando una instancia de WebView intenta cargar una página que Google clasificó como amenaza conocida, la WebView muestra de forma predeterminada un intersticial que advierte a los usuarios acerca de la amenaza. Esta pantalla da a los usuarios la opción de cargar la URL de todas maneras o regresar a una página anterior que sea segura.
Si desarrollas tu app para Android 8.1 (nivel de API 27) o versiones posteriores, puedes definir de manera programática la forma en que responderá a una amenaza conocida de las siguientes maneras:
- Puedes controlar si tu app informará amenazas conocidas a Navegación segura.
- Puedes hacer que tu app realice automáticamente una acción determinada, como regresar a un estado seguro, cada vez que encuentre una URL clasificada como amenaza conocida.
En los siguientes fragmentos de código, se muestra la instrucción que puedes dar a las instancias de WebView de tu app para que siempre regresen a un estado seguro tras encontrar una amenaza conocida:
MyWebActivity.java
Kotlin
private lateinit var superSafeWebView: WebView private var safeBrowsingIsInitialized: Boolean = false // ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) superSafeWebView = WebView(this) superSafeWebView.webViewClient = MyWebViewClient() safeBrowsingIsInitialized = false if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) { WebViewCompat.startSafeBrowsing(this, ValueCallback<Boolean> { success -> safeBrowsingIsInitialized = true if (!success) { Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!") } }) } }
Java
private WebView superSafeWebView; private boolean safeBrowsingIsInitialized; // ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); superSafeWebView = new WebView(this); superSafeWebView.setWebViewClient(new MyWebViewClient()); safeBrowsingIsInitialized = false; if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) { WebViewCompat.startSafeBrowsing(this, new ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean success) { safeBrowsingIsInitialized = true; if (!success) { Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!"); } } }); } }
MyWebViewClient.java
Kotlin
class MyWebViewClient : WebViewClientCompat() { // Automatically go "back to safety" when attempting to load a website that // Google identifies as a known threat. An instance of WebView calls this // method only after Safe Browsing is initialized, so there's no conditional // logic needed here. override fun onSafeBrowsingHit( view: WebView, request: WebResourceRequest, threatType: Int, callback: SafeBrowsingResponseCompat ) { // The "true" argument indicates that your app reports incidents like // this one to Safe Browsing. if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) { callback.backToSafety(true) Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show() } } }
Java
public class MyWebViewClient extends WebViewClientCompat { // Automatically go "back to safety" when attempting to load a website that // Google identifies as a known threat. An instance of WebView calls this // method only after Safe Browsing is initialized, so there's no conditional // logic needed here. @Override public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponseCompat callback) { // The "true" argument indicates that your app reports incidents like // this one to Safe Browsing. if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) { callback.backToSafety(true); Toast.makeText(view.getContext(), "Unsafe web page blocked.", Toast.LENGTH_LONG).show(); } } }
API de HTML5 Geolocation
En el caso de apps desarrolladas para Android 6.0 (nivel de API 23) y versiones posteriores, la API de Geolocation solo es compatible con orígenes seguros, como HTTPS. Cualquier solicitud a la API de Geolocation desde orígenes no seguros se rechaza automáticamente sin invocar el método onGeolocationPermissionsShowPrompt() correspondiente.
Cómo inhabilitar la recopilación de métricas
WebView tiene la capacidad de subir datos de diagnóstico anónimos a Google cuando el usuario da su consentimiento. Los datos se recopilan por app para cada aplicación que crea una instancia de WebView. Puedes inhabilitar esta
función si creas la siguiente etiqueta en el elemento del manifiesto:
<application>
<manifest> <application> ... <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" /> </application> </manifest>
Los datos solo se subirán desde una app si el usuario da su consentimiento y la aplicación no inhabilita la función. Para obtener más información sobre cómo inhabilitar los informes de datos de diagnóstico, consulta Privacidad del usuario en los informes de WebView.
API de Termination Handling
La API de Termination Handling maneja casos en los que desaparece el proceso del procesador para un objeto WebView, ya sea porque el sistema cierra el procesador para recuperar memoria que necesita o porque falla el proceso del procesador. Si usas esta API, permites que tu app se siga ejecutando, incluso aunque el proceso del procesador haya desaparecido.
Si un procesador falla durante la carga de una página web determinada intentar cargar la misma página puede causar que un nuevo objeto WebView muestre el mismo comportamiento de falla de procesamiento.
En el siguiente fragmento de código, se ilustra cómo usar esta API dentro de una Activity:
Kotlin
inner class MyRendererTrackingWebViewClient : WebViewClient() { private var mWebView: WebView? = null override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean { if (!detail.didCrash()) { // Renderer is killed because the system ran out of memory. The app // can recover gracefully by creating a new WebView instance in the // foreground. Log.e("MY_APP_TAG", ("System killed the WebView rendering process " + "to reclaim memory. Recreating...")) mWebView?.also { webView -> val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container) webViewContainer.removeView(webView) webView.destroy() mWebView = null } // By this point, the instance variable "mWebView" is guaranteed to // be null, so it's safe to reinitialize it. return true // The app continues executing. } // Renderer crashes because of an internal error, such as a memory // access violation. Log.e("MY_APP_TAG", "The WebView rendering process crashed!") // In this example, the app itself crashes after detecting that the // renderer crashed. If you handle the crash more gracefully and let // your app continue executing, you must destroy the current WebView // instance, specify logic for how the app continues executing, and // return "true" instead. return false } }
Java
public class MyRendererTrackingWebViewClient extends WebViewClient { private WebView mWebView; @Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { if (!detail.didCrash()) { // Renderer is killed because the system ran out of memory. The app // can recover gracefully by creating a new WebView instance in the // foreground. Log.e("MY_APP_TAG", "System killed the WebView rendering process " + "to reclaim memory. Recreating..."); if (mWebView != null) { ViewGroup webViewContainer = (ViewGroup) findViewById(R.id.my_web_view_container); webViewContainer.removeView(mWebView); mWebView.destroy(); mWebView = null; } // By this point, the instance variable "mWebView" is guaranteed to // be null, so it's safe to reinitialize it. return true; // The app continues executing. } // Renderer crashes because of an internal error, such as a memory // access violation. Log.e("MY_APP_TAG", "The WebView rendering process crashed!"); // In this example, the app itself crashes after detecting that the // renderer crashed. If you handle the crash more gracefully and let // your app continue executing, you must destroy the current WebView // instance, specify logic for how the app continues executing, and // return "true" instead. return false; } }
API de Renderer Importance
Cuando los objetos WebView
funcionan en el modo multiproceso, tienes cierta flexibilidad con respecto a cómo manejará tu app las situaciones de falta de memoria. Puedes usar la API de Renderer Importance, que se introdujo en Android 8.0, y establecer una política de prioridad para el procesador asignado a un objeto WebView particular. En particular, es posible que desees que la parte principal de tu app se siga ejecutando cuando se cierra un procesador que muestra los objetos WebView de la aplicación. Puedes hacer esto, por ejemplo, si esperas no mostrar el objeto WebView durante un largo período para que el sistema pueda recuperar la memoria que estaba utilizando el procesador.
En el siguiente fragmento de código, se muestra cómo asignar una prioridad al proceso del procesador asociado con los objetos WebView de tu app:
Kotlin
val myWebView: WebView = ... myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)
Java
WebView myWebView; myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);
En este fragmento en particular, la prioridad del procesador es la misma que la prioridad predeterminada de la app o está vinculada a ella. El argumento true disminuye la prioridad del procesador a RENDERER_PRIORITY_WAIVED cuando el objeto WebView asociado ya no está visible. En otras palabras, el argumento true indica que tu app no tiene en cuenta si el sistema mantiene activo el proceso del procesador. De hecho, este nivel de prioridad más bajo hace que sea probable que el proceso del procesador se cierre en situaciones de falta de memoria.
Para obtener más información sobre el diagnóstico y la optimización del espacio en memoria de tu app cuando usas contenido web, consulta Administra y diagnostica la memoria de WebView. Para obtener más información sobre cómo maneja el sistema las situaciones de poca memoria en los procesos, consulta Ciclo de vida de procesos y aplicaciones.
Cómo conservar el estado y el historial de navegación
Cuando el sistema recupera recursos en segundo plano o el dispositivo experimenta un cambio de configuración, es posible que se destruyan la actividad de una app y sus objetos WebView. Para conservar el contexto de navegación del usuario,
usa el saveState(Bundle)
método dentro de onSaveInstanceState() para serializar el historial de navegación en un
Bundle y, luego, restáuralo con restoreState(Bundle).
Debido a que Android aplica un límite estricto de 1 MB en todo el proceso para savedInstanceState, la serialización de sesiones de navegación grandes con la API de framework estándar puede generar una TransactionTooLargeException.
Para aplicar límites de tamaño o descartar entradas de historial de avance de forma segura, usa el método WebViewCompat.saveState() de Jetpack Webkit.
Para obtener estrategias detalladas y prácticas recomendadas sobre la administración eficiente del estado, consulta Administra el estado de WebView de manera eficiente.