Android proporciona varias API para ayudarte a administrar los objetos WebView
que muestran contenido web en tu app.
En esta página, se describe cómo usar las API 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 (API nivel 24), los usuarios pueden elegir entre varios paquetes diferentes para mostrar contenido web en un objeto WebView
. La biblioteca WebKit de AndroidX incluye el método
getCurrentWebViewPackage()
para obtener información relacionada con el paquete que muestra contenido web en tu app. Este método es especialmente ú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);
Nota: El método getCurrentWebViewPackage()
puede mostrar null
si el dispositivo se configuró de forma incorrecta, si no admite el uso de WebView
(como un dispositivo con Wear OS) o si falta una implementación de WebView
actualizable, como en Android 4.4 (API nivel 19) y versiones anteriores.
Servicio de Navegación segura de Google
Para que los usuarios obtengan una experiencia de navegación más segura, tus objetos WebView
verifican las URL mediante la Navegación segura de Google , que permite que tu app les muestre una advertencia cuando intentan visitar un sitio web potencialmente inseguro.
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.
WebView
inhabiliten las verificaciones de la Navegación segura, puedes agregar el siguiente elemento <meta-data>
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 (API nivel 27) o versiones posteriores, puedes definir de manera programática la forma en que responderá a una amenaza conocida:
- 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.
Nota: A fin de contar con máxima protección contra amenazas conocidas, espera hasta haber inicializado la Navegación segura para invocar el método loadUrl()
de un objeto WebView
.
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 has identified 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 has identified 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 (API nivel 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 creó una instancia de WebView
. Puedes inhabilitar esta función si creas la siguiente etiqueta en el elemento <application>
del manifiesto:
<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 dio su consentimiento y la aplicación no inhabilitó la función.
API de Termination Handling
Esta API maneja casos en los que desaparece el proceso del procesador para un objeto WebView
, ya sea porque el sistema cerró el procesador para recuperar memoria que necesitaba o porque falló el proceso del procesador. Si usas esta API, permites que tu app se siga ejecutando, incluso aunque el proceso del procesador haya desaparecido.
Precaución: Si tu app se sigue ejecutando tras desaparecer el proceso del procesador, la instancia asociada de WebView
no se podrá reutilizar, independientemente de que el proceso del procesador se cierre o falle. Tu app debe quitar la instancia de la jerarquía de vistas y destruirla para continuar ejecutándose. Luego, debe crear una instancia completamente nueva de WebView
para seguir renderizando páginas web.
Ten en cuenta que 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:
Kotlin
inner class MyRendererTrackingWebViewClient : WebViewClient() { private var mWebView: WebView? = null override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean { if (!detail.didCrash()) { // Renderer was 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 crashed 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 choose to handle the crash more gracefully // and allow your app to continue executing, you should 1) destroy the // current WebView instance, 2) specify logic for how the app can // continue executing, and 3) 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 was 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 crashed 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 choose to handle the crash more gracefully // and allow your app to continue executing, you should 1) destroy the // current WebView instance, 2) specify logic for how the app can // continue executing, and 3) return "true" instead. return false; } }
API de Renderer Importance
Ahora que 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 particular, la prioridad del procesador es la misma que la predeterminada para la app (o "está vinculada a" dicha prioridad). El argumento true
reduce 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.
Advertencia: A fin de mantener la estabilidad de la app, no debes cambiar la política de prioridad del procesador para un objeto WebView
a menos que también uses la API de Termination Handling para especificar cómo reacciona WebView
cuando desaparece su procesador asociado.
Para obtener más información sobre cómo maneja el sistema las situaciones de poca memoria, consulta el artículo Ciclo de vida de procesos y aplicaciones.