Como gerenciar objetos do WebView

O Android fornece várias APIs para ajudar você a gerenciar os objetos WebView que exibem conteúdo da Web no seu app.

Esta página descreve como usar essas APIs para trabalhar com objetos WebView com mais eficiência, melhorando a estabilidade e a segurança do app.

API Version

A partir do Android 7.0 (API de nível 24), os usuários podem escolher entre vários pacotes diferentes para exibir conteúdo da Web em um objeto WebView. A biblioteca androidX webKit inclui o método getCurrentWebViewPackage() para buscar informações relacionadas ao pacote que está exibindo conteúdo da Web no seu app. Esse método é particularmente útil ao analisar erros que ocorrem apenas quando o app tenta exibir conteúdo da Web usando a implementação de um pacote específico da WebView.

Para usar esse método, adicione a lógica mostrada no snippet de código a seguir:

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);
    

Observação: o método getCurrentWebViewPackage() pode retornar como null se o dispositivo tiver sido configurado incorretamente, não for compatível com o uso de WebView (como um dispositivo com Wear OS) ou não tiver uma implementação WebView atualizável, como no Android 4.4 (API de nível 19) e versões anteriores.

Serviço de Navegação segura do Google

Para oferecer uma experiência de navegação mais segura aos usuários, os objetos WebView verificam URLs usando o serviço Navegação segura do Google, que permite que o app exiba um aviso aos usuários quando eles tentarem navegar para um site potencialmente inseguro.

Enquanto o valor padrão de EnableSafeBrowsing for verdadeiro, haverá casos ocasionais em que você poderá querer ativar o "Navegação segura" apenas condicionalmente ou desativá-lo. O Android 8.0 (API de nível 26) e as versões posteriores são compatíveis com o uso de setSafeBrowsingEnabled() para alternar o "Navegação segura" para um objeto WebView individual.

Se você quiser que todos os objetos WebView desativem as verificações do "Navegação segura", adicione o seguinte elemento <meta-data> ao arquivo de manifesto do app:

    <manifest>
        <application>
            <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                       android:value="false" />
            ...
        </application>
    </manifest>
    

Como definir ações programáticas

Quando uma instância de WebView tenta carregar uma página que foi classificada pelo Google como uma ameaça conhecida, a WebView mostra, por padrão, um intersticial que avisa os usuários sobre a ameaça. Essa tela dá aos usuários a opção de carregar o URL mesmo assim ou de retornar à página segura anterior.

Se seu objetivo for o Android 8.1 (API de nível 27) ou versões posteriores, você pode definir programaticamente como seu app responde a uma ameaça conhecida:

  • Você pode controlar se o aplicativo reporta as ameaças conhecidas para o "Navegação segura".
  • Você pode fazer com que o app realize automaticamente uma ação específica, como voltar à segurança, sempre que encontrar um URL classificado como uma ameaça conhecida.

Observação: para conseguir a melhor proteção contra ameaças conhecidas, aguarde até inicializar o "Navegação segura" antes de invocar um método loadUrl() do objeto WebView.

Os snippets de código a seguir mostram como você pode instruir as instâncias de WebView do app a sempre voltar à segurança depois de encontrar uma ameaça conhecida:

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 HTML5 Geolocation

Para apps voltados ao Android 6.0 (API de nível 23) ou versões posteriores, a API Geolocation só é compatível com origens seguras, como o HTTPS. Qualquer solicitação para a API Geolocation em origens não seguras é automaticamente negada, sem invocar o método onGeolocationPermissionsShowPrompt() correspondente.

Como desativar a coleta de métricas

A WebView consegue fazer upload de dados de diagnóstico anônimos para o Google diante do consentimento do usuário. Os dados são coletados em cada app para cada aplicativo que instanciar uma WebView. Você pode desativar esse recurso criando a seguinte tag no elemento <application> do manifesto:

    <manifest>
        <application>
        ...
        <meta-data android:name="android.webkit.WebView.MetricsOptOut"
                   android:value="true" />
        </application>
    </manifest>
    

O upload dos dados só será feito a partir de um app se o usuário consentir e se o app não tiver desativado a opção.

API Termination Handling

Essa API gerencia casos em que o processo do renderizador de um objeto WebView desaparece, seja porque o sistema interrompeu o renderizador para reivindicar a memória necessária ou porque o processo do renderizador falhou. Ao usar essa API, você permite que seu app continue em execução, mesmo que o processo do renderizador tenha sido removido.

Atenção: se o app continuar em execução após o fim do processo do renderizador, a instância associada de WebView não poderá ser reutilizada, independentemente da interrupção ou do travamento do processo do renderizador. Seu app precisa remover a instância da hierarquia de visualização e destruí-la para continuar a execução. Depois disso, o app precisa criar uma instância inteiramente nova de WebView para continuar processando páginas da Web.

Se um renderizador travar ao carregar uma determinada página da Web, tentar carregar a mesma página novamente poderá fazer com que um novo objeto WebView exiba o mesmo comportamento de falha de renderização.

O snippet de código a seguir ilustra como usar essa 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 Renderer Importance

Agora que objetos WebView operam no modo multiprocesso, você tem certa flexibilidade na forma como o app gerencia situações de falta de memória. Você pode usar a API Renderer Importance, introduzida no Android 8.0, para definir uma política de prioridade para o renderizador atribuído a um objeto WebView. Especificamente, você pode querer que a parte principal do app continue em execução quando um renderizador que exibe os objetos WebView dele for eliminado. Você pode fazer isso, por exemplo, se não mostrar o objeto WebView por um longo período para que o sistema possa reivindicar a memória que o renderizador estava usando.

O snippet de código a seguir mostra como atribuir uma prioridade ao processo do renderizador associado aos objetos WebView do app:

Kotlin

    val myWebView: WebView = ...
    myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)
    

Java

    WebView myWebView;
    myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);
    

Nesse snippet, a prioridade do renderizador é a mesma que a prioridade padrão do app ou está vinculada a ela. O argumento true diminui a prioridade do renderizador para RENDERER_PRIORITY_WAIVED quando o objeto WebView associado não está mais visível. Em outras palavras, um argumento true indica que, para o app, não faz diferença se o sistema mantém o processo do renderizador ativo. Esse nível de prioridade menor torna provável que o processo do renderizador seja interrompido em situações de falta de memória.

Aviso: para manter a estabilidade do app, não mude a política de prioridade do renderizador para um objeto WebView, a menos que você também use a API Termination Handle para especificar de que forma WebView reage quando o renderizador associado desaparece.

Para saber mais sobre como o sistema gerencia situações de pouca memória, consulte Processos e ciclo de vida do app.