Gerenciar objetos WebView

O Android oferece 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 de forma mais eficaz, melhorando a estabilidade e a segurança do app.

API Version

A partir do Android 7.0 (nível 24 da API), os usuários podem escolher entre vários pacotes diferentes para mostrar 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á mostrando conteúdo da Web no seu app. Esse método é útil para analisar erros que ocorrem apenas quando o app tenta exibir conteúdo da Web usando a implementação de WebView de um pacote específico.

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

Serviço de Navegação segura do Google

Para oferecer aos usuários uma experiência de navegação mais segura, os objetos WebView verificam os URLs usando a Navegação segura do Google, que permite que seu app mostre um aviso aos usuários quando eles tentarem acessar um site possivelmente não seguro.

Embora o valor padrão de EnableSafeBrowsing seja "true", há casos em que é recomendável ativar a Navegação segura apenas condicionalmente ou desativá-la. O Android 8.0 (nível 26 da API) e versões mais recentes oferece suporte ao uso de setSafeBrowsingEnabled() para ativar a Navegação segura para um objeto WebView individual.

Se você quiser que todos os objetos WebView desativem as verificações da 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>

Definir ações programáticas

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

Se seu app for destinado ao Android 8.1 (API de nível 27) ou versões mais recentes, você poderá definir de maneira programática como o app responde a uma ameaça conhecida das seguintes maneiras:

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

Os snippets de código abaixo mostram como instruir as instâncias de WebView do app a sempre voltar a um estado seguro após 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 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 HTML5 Geolocation

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

Desativar a coleta de métricas

O WebView pode fazer o upload de dados de diagnóstico anônimos para o Google quando o usuário consente. Os dados são coletados por app para cada app que instancia uma WebView. É possível desativar esse recurso criando a tag abaixo no elemento <application> do manifesto:

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

Os dados só são enviados de um app se o usuário consentir e não desativar o app. Para mais informações sobre como desativar os relatórios de dados de diagnóstico, consulte Privacidade do usuário nos relatórios da WebView.

API Termination Handling

A API Rescisão Handling processa casos em que o processo do renderizador para um objeto WebView desaparece porque o sistema encerra o renderizador para reivindicar a memória necessária ou porque o processo do renderizador falha. Ao usar essa API, você permite que o app continue em execução, mesmo que o processo do renderizador desapare.

Se um renderizador falhar ao carregar uma página da Web específica, uma tentativa de carregar essa mesma página novamente poderá fazer com que um novo objeto WebView exponha o mesmo comportamento de falha de renderização.

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

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

O snippet de código abaixo mostra como atribuir uma prioridade ao processo do renderizador associado aos objetos WebView do seu app.

Kotlin

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

Java

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

Nesse snippet específico, 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 estiver mais visível. Em outras palavras, um argumento true indica que seu app não se importa se o sistema mantém o processo do renderizador ativo. Na verdade, esse nível de prioridade mais baixo faz com que o processo do renderizador seja encerrado em situações de falta de memória.

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