Gerenciar objetos WebView

O Android fornece diversas APIs para ajudar você a gerenciar os objetos WebView que exibem conteúdo da Web no seu aplicativo.

Esta página descreve como usar essas APIs para trabalhar com objetos WebView com mais eficácia, melhorando a estabilidade e a segurança do seu aplicativo.

Versão da API

A partir do Android 7.0 (nível de API 24), os usuários podem escolher entre vários pacotes diferentes para exibir conteúdo da Web em um objeto WebView. O Android 8.0 (nível de API 26) e versões posteriores incluem uma API para recuperar informações relacionadas ao pacote que exibe conteúdo da Web no seu aplicativo. Essa API é especialmente útil ao analisar erros que ocorrem somente quando seu aplicativo tenta exibir conteúdo da Web usando a implementação de WebView de um pacote específico.

Para usar essa API, adicione a lógica mostrada no seguinte snippet de código:

Kotlin

val webViewPackageInfo = WebView.getCurrentWebViewPackage()
Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")

Java

PackageInfo webViewPackageInfo = WebView.getCurrentWebViewPackage();
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);

Observação: o método WebView.getCurrentWebViewPackage() pode retornar null se o dispositivo não tiver sido configurado corretamente. Ele também retorna null se você estiver executando seu aplicativo em um dispositivo que não oferece suporte a WebView, como um dispositivo Wear OS.

API de navegação segura do Google

Para fornecer aos usuários uma experiência de navegação mais segura, seus objetos WebView verificam URLs que utilizem o Navegação segura do Google. Isso permite que o seu aplicativo exiba aos usuários um aviso quando eles tentarem navegar em sites perigosos.

Embora o valor padrão de EnableSafeBrowsing seja verdadeiro, existem situações ocasionais onde é possível ativar somente o Navegação segura condicionalmente ou desativá-lo. Suporte para o Android 8.0 (nível de API 26) ou posterior, usando setSafeBrowsingEnabled(). Aplicativos compilados a níveis de API mais baixos não podem usar setSafeBrowsingEnabled() e devem alterar o valor de EnableSafeBrowsing no manifesto para falso, a fim de desativar o recurso para todas as instâncias de WebView.

Caso seu aplicativo esteja direcionado ao Android 7.1 (nível de API 25) ou inferior, será possível optar que seus objetos WebView não verifiquem os URLs na lista de sites perigosos do Navegação segura do Google ao adicionar o elemento <meta-data> a seguir ao arquivo de manifesto:

<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 tentar carregar uma página que foi 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 conhecida. Essa tela dá aos usuários a opção de carregar o URL mesmo assim ou de retornar a página anterior segura.

Se você direcionar seu aplicativo para o Android 8.1 (nível de API 27) ou para uma versão posterior, será possível definir de forma programática como o aplicativo responde a uma ameaça conhecida:

  • Você pode controlar se seu aplicativo reporta as ameaças conhecidas para o Navegação segura.
  • Seu aplicativo pode executar uma ação específica automaticamente, como voltar para um local seguro, sempre que encontrar uma URL classificada como uma ameaça conhecida.

Observação: para otimizar sua proteção contra ameaças conhecidas, espere até que o Navegação segura seja inicializado antes de invocar o método loadUrl() de um objeto WebView.

O snippet de código a seguir mostra como você pode instruir as instâncias de WebView do seu aplicativo a sempre voltar a um local 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

    superSafeWebView.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;

    superSafeWebView.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 : WebViewClient() {
    // 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: SafeBrowsingResponse
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true)
        Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
    }
}

Java

public class MyWebViewClient extends WebViewClient {
    // 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, SafeBrowsingResponse callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true);
        Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                Toast.LENGTH_LONG).show();
    }
}

API HTML5 Geolocation

Para aplicativos direcionados para o Android 6.0 (nível de API 23) ou superior, a API Geolocation só recebe suporte em origens seguras, como HTTPS. Qualquer solicitação para a API Geolocation em origens não seguras é negada automaticamente sem invocar o método onGeolocationPermissionsShowPrompt() correspondente.

Cancelar a coleção de métricas

WebView faz o upload de dados de diagnóstico anônimos para o Google quando o usuário autoriza. Os dados são coletados por cada aplicativo que tenha instanciado um WebView. É possível cancelar esse recurso ao criar a tag a seguir no elemento <application> do manifesto:

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

Os dados só serão atualizados a partir de um aplicativo se o usuário autorizar e se o recurso não tiver sido cancelado.

API Termination Handling

Essa API processa casos em que o processo do renderizador para um objeto WebView desaparece, seja porque o sistema anulou o renderizador para reivindicar memória necessária ou porque o processo correspondente falhou. Ao usar essa API, você permite que seu aplicativo continue a ser executado, mesmo que o processo do renderizador tenha desaparecido.

Atenção: Se seu aplicativo continuar a ser executado após o desaparecimento do processo do renderizador, a instância associada de WebView não poderá ser renderizada, independentemente de o processo do renderizador ter falhado ou sido anulado. Seu aplicativo deve remover a instância da hierarquia de visualização e destruí-la para continuar a ser executado. Em seguida, o aplicativo precisa criar uma instância totalmente nova de WebView para continuar a renderizar páginas da Web.

Saiba que, se um renderizador falhar enquanto carrega uma determinada página da Web, a tentativa de carregar um novo objeto WebView poderá causar erros.

O snippet de código a seguir mostra 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 os objetos WebView operam em um modo multiprocesso, você tem alguma flexibilidade em como seu aplicativo processa situações fora da 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 em particular. Especificamente, você pode querer que a parte principal do seu aplicativo continue a ser executada quando um renderizador que exibe os objetos WebView for anulado. Por exemplo, isso pode ser feito caso você não queira mostrar o objeto WebView por um longo período para que o sistema possa reivindicar a memória usada pelo renderizador.

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

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 (ou “está vinculada a”) a prioridade padrão do aplicativo. O argumento true reduz 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 aplicativo não se importa se o sistema mantém o processo do renderizador ativo. Na verdade, essa prioridade mais baixa aumenta a probabilidade de o processo do renderizador ser anulado em situações fora de memória.

Aviso: Para manter a estabilidade do aplicativo, não altere a política de renderização para um objeto WebView, a menos que também use a API Termination Handle para especificar como o WebView reage quando o renderizador associado desaparece.

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