Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo administrar objetos WebView

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.

Si deseas que todos los objetos 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.