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 estas 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 de Jetpack Webkit incluye el método getCurrentWebViewPackage() para recuperar información relacionada con el paquete que muestra contenido web en tu app. Este método es útil cuando se analizan errores que ocurren solo cuando tu app intenta mostrar contenido web con la implementación de WebView de un paquete en particular.
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, los objetos WebView verifican las URLs con la Navegación segura de Google, que permite que tu app les muestre una advertencia cuando intentan navegar a 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. Android 8.0 (nivel de API 26) y versiones posteriores admiten el uso de setSafeBrowsingEnabled() para activar o desactivar la Navegación segura en un objeto WebView individual.
Si deseas que todos los objetos WebView inhabiliten las verificaciones de la Navegación segura, agrega 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 (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 las apps segmentadas 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 <application> del manifiesto:
<manifest> <application> ... <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" /> </application> </manifest>
Los datos solo se suben desde una app si el usuario da su consentimiento y la app no inhabilita la función. Para obtener más información sobre cómo inhabilitar el envío de informes de datos de diagnóstico, consulta Privacidad del usuario en los informes de WebView.
API de Termination Handling
La API de Termination Handling controla los casos en los que desaparece el proceso del renderizador para un objeto WebView, ya sea porque el sistema cierra el renderizador para recuperar la memoria necesaria o porque falla el proceso del renderizador. Si usas esta API, permites que tu app se siga ejecutando, incluso aunque el proceso del renderizador desaparezca.
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 un 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 operan en modo multiproceso, tienes cierta flexibilidad en la forma en que tu app controla las situaciones de falta de memoria. Puedes usar la API de Renderer Importance, que se introdujo en Android 8.0, para establecer una política de prioridad para el procesador asignado a un objeto WebView en 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. Podrías hacer esto, por ejemplo, si esperas no mostrar el objeto WebView durante mucho tiempo para que el sistema pueda recuperar la memoria que estaba usando 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, un argumento true indica que tu app no tiene en cuenta si el sistema mantiene activo el proceso del renderizador. De hecho, este nivel de prioridad más bajo hace que sea probable que el proceso del renderizador se cierre en situaciones de falta de memoria.
Para obtener más información sobre cómo maneja el sistema las situaciones de poca memoria, consulta Ciclo de vida de procesos y aplicaciones.