Quando o app usa um WebView pela primeira vez, o sistema realiza tarefas de inicialização específicas.
Esse processo de inicialização é pesado. Por padrão, isso acontece implicitamente na
thread da interface do usuário na primeira vez que o aplicativo chama muitas APIs nos pacotes
android.webkit ou androidx.webkit ou aumenta um layout que
contém uma tag WebView.
Por que isso é importante
Como essa inicialização implícita acontece inteiramente na linha de execução principal, ela impede que o app processe a entrada do usuário e aumenta drasticamente o risco de erros "O app não está respondendo" (ANR, na sigla em inglês). Para mais informações sobre como o Android processa o modelo de execução de linha única, consulte Visão geral de processos e linhas de execução.
Acionadores para inicialização implícita
A inicialização implícita pode ser acionada das seguintes maneiras:
- Programaticamente: chamando APIs como
WebSettings.getUserAgentString(). - Usando layouts: chamar
setContentView()oulayoutInflater.inflate()em um recurso XML que inclui um<WebView>.
A inicialização implícita também pode afetar negativamente as métricas de negócios, como o tempo de inicialização do app e o tempo até a primeira exibição. Se a inicialização implícita não for
ideal para seu app, use startUpWebView.
Esta página discute como otimizar o desempenho de inicialização do WebView usando a
API startUpWebView.
Assumir o controle da inicialização da WebView
Para melhorar o desempenho e minimizar ANRs, use a API startUpWebView disponível
na biblioteca Jetpack Webkit. Essa API oferece controle explícito sobre quando
a WebView é iniciada. Ele transfere uma quantidade significativa da carga de trabalho de inicialização para uma
linha de execução em segundo plano e permite que qualquer trabalho que precise acontecer na linha de execução de interface seja
feito em partes, em vez de um grande bloco monolítico. Isso libera sua
thread da interface para processar outras tarefas críticas do app em paralelo, reduzindo a chance de
bloquear a experiência do usuário.
A API usa o callback androidx.webkit.WebViewOutcomeReceiver, permitindo que você
acompanhe as inicializações bem-sucedidas.
Para usar essa API, adicione a biblioteca Jetpack Webkit ao arquivo build.gradle.
Verifique se você está usando a versão 1.16.0 ou mais recente:
dependencies {
implementation("androidx.webkit:webkit:1.16.0")
}
Usar a API startUpWebView
A otimização do fluxo de inicialização depende do momento em que o app precisa mostrar a WebView.
Quando o WebView não está no caminho crítico
Se o app não precisar carregar um WebView imediatamente, você poderá ocultar completamente o custo de inicialização. Chame startUpWebView no início do ciclo de vida do app e aguarde o callback de sucesso ser acionado.
O ideal é esperar o callback antes de chamar outras APIs WebView. Se você acionar startUpWebView, mas não esperar que ele termine antes de tocar em outros componentes WebView, o sistema vai bloquear a linha de execução de interface enquanto aguarda a conclusão da inicialização. Seu app pode ter algum benefício de desempenho com
o trabalho em segundo plano já concluído, mas não o máximo possível.
Quando o WebView está no caminho crítico
Se a jornada principal do usuário do app exigir um WebView imediatamente, provavelmente
não será possível esperar a conclusão da inicialização do WebView. Nesse cenário, ainda é necessário chamar startUpWebView o mais cedo possível no ciclo de vida do app (como em Application.onCreate), mas não espere o callback ser acionado. Em vez disso, use as APIs WebView diretamente quando elas forem necessárias.
Para aproveitar ao máximo a inicialização assíncrona, adie a instanciação de uma WebView ou a chamada de APIs WebView até que não haja mais operações de linha de execução de interface de caminho crítico para serem executadas, como inflar hierarquias de layout, inicializar outros SDKs ou desenhar o frame inicial.
Se você chamar startUpWebView e invocar imediatamente as APIs WebView depois na linha de execução principal, a linha de execução de interface será bloqueada aguardando a inicialização ser concluída. Nesse cenário, não há benefício de performance.
Se o uso do WebView puder entrar no caminho crítico, mas você não quiser iniciar
o WebView por completo, poderá executar seletivamente as tarefas de inicialização do WebView
que podem ser executadas em uma linha de execução em segundo plano, liberando a linha de execução da interface para
outras tarefas críticas do app. Para isso, use
shouldRunUiThreadStartUpTasks(false).
Mais tarde no ciclo de vida do app, você pode chamar startUpWebView novamente com
shouldRunUiThreadStartUpTasks(true) para concluir as tarefas de inicialização restantes na
linha de execução da interface. Aguardar o callback nesse ponto depende de
se o uso do WebView está no caminho crítico.
Exemplo de implementação
A API usa o callback androidx.webkit.WebViewOutcomeReceiver, permitindo que você
acompanhe inicializações bem-sucedidas ou processe falhas de diagnóstico.
É seguro chamar startUpWebView várias vezes de diferentes partes do seu
app. Recomendamos evitar a implementação de um loop de nova tentativa simples.
O exemplo de código a seguir demonstra como usar a API
WebViewCompat.startUpWebView para inicialização assíncrona.
Kotlin
import android.content.Context
import android.util.Log
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewOutcomeReceiver
import androidx.webkit.WebViewStartUpConfig
import androidx.webkit.WebViewStartUpResult
import androidx.webkit.WebViewStartupException
import java.util.concurrent.Executors
fun initializeWebView(context: Context) {
// 1. Create a startup configuration specifying the background thread
// that WebView will use to run its initialization tasks.
val startUpConfig = WebViewStartUpConfig.Builder(
Executors.newSingleThreadExecutor()
).build()
// 2. Trigger WebView startup asynchronously
WebViewCompat.startUpWebView(
context,
startUpConfig,
object : WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException> {
override fun onResult(result: WebViewStartUpResult) {
// Success: The WebView has finished its background initialization.
// This callback is guaranteed to be invoked on the UI thread.
setupWebView()
}
override fun onError(error: WebViewStartupException) {
// Failure: The initialization encountered a startup exception.
Log.e("WebViewStartup", "Failed to initialize WebView", error)
}
}
)
}
Java
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewOutcomeReceiver;
import androidx.webkit.WebViewStartUpConfig;
import androidx.webkit.WebViewStartUpResult;
import androidx.webkit.WebViewStartupException;
import java.util.concurrent.Executors;
public void initializeWebView(Context context) {
// 1. Create the startup configuration specifying the background thread pool
// to handle internal non-UI initialization processes.
WebViewStartUpConfig startUpConfig = new WebViewStartUpConfig.Builder(
Executors.newSingleThreadExecutor()
).build();
// 2. Trigger WebView startup asynchronously
WebViewCompat.startUpWebView(
context,
startUpConfig,
new WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException>() {
@Override
public void onResult(@NonNull WebViewStartUpResult result) {
// Success: The WebView has finished its background initialization.
// This callback is invoked directly on the UI thread.
setupWebView();
}
@Override
public void onError(@NonNull WebViewStartupException error) {
// Failure: Handled using the concrete WebViewStartupException
Log.e("WebViewStartup", "Failed to initialize WebView", error);
}
}
);
}
Depurar problemas de inicialização assíncrona
Se startUpWebView não gerar os benefícios de desempenho esperados, geralmente é porque a WebView está sendo inicializada implicitamente em outro lugar do app antes da execução da sua chamada. Isso pode ser devido aos seguintes motivos:
Bibliotecas de terceiros ou SDKs inicializados no início do ciclo de vida do app.
ContentProvidersinjetado no seu APK que aciona as APIs WebView durante a inicialização do app.Inflações de layout ou chamadas programáticas (como buscar strings de user agent) que ocorrem inesperadamente cedo.
Para ajudar você a diagnosticar onde e por que essas inicializações inesperadas estão
acontecendo, o objeto WebViewStartUpResult oferece recursos de auditoria
integrados:
getUiThreadBlockingStartUpLocations(): retorna uma lista de objetosStartUpLocationque representam locais em que as tarefas de inicialização do WebView bloquearam a linha de execução da interface principal.getNonUiThreadBlockingStartUpLocations(): retorna sites de chamada específicos em que a execução de tarefas de inicialização bloqueou linhas de execução em segundo plano.
Cada StartUpLocation contém um stack trace que pode ser registrado ou inspecionado para encontrar a classe e o método exatos que acionaram a inicialização.
Exemplo de implementação
É possível inspecionar esses locais no callback onResult para auditar o
caminho de inicialização:
override fun onResult(result: WebViewStartUpResult) {
// Check if WebView startup was blocked on the UI thread prior to or during initialization
val uiBlockingLocations = result.getUiThreadBlockingStartUpLocations()
if (!uiBlockingLocations.isNullOrEmpty()) {
for (location in uiBlockingLocations) {
// Log the stack trace of the call site that triggered the UI-blocking startup
Log.w("WebViewDebug", "WebView startup blocked the UI thread here:", location.getStack())
}
} else {
Log.i("WebViewDebug", "Excellent! No UI-blocking WebView startup detected.")
}
// Check where background initialization tasks were executed
val backgroundLocations = result.getNonUiThreadBlockingStartUpLocations()
backgroundLocations?.forEach { location ->
Log.d("WebViewDebug", "WebView background startup occurred at: ${location.getStack()}")
}
setupWebView()
}
Como usar esses dados durante uma auditoria
Ao auditar a inicialização da WebView do seu app, use as seguintes estratégias para analisar os dados de diagnóstico e resolver gargalos de desempenho:
Procure rastreamentos de pilha inesperados:se
getUiThreadBlockingStartUpLocations()não estiver vazio, confira os rastreamentos de pilha impressos. Se você encontrar classes pertencentes a SDKs de terceiros ou componentes inesperados, encontrou um gargalo de inicialização implícita.Verifique a ordem de chamada:se as saídas de registro mostrarem que uma inicialização implícita ocorreu antes da sua chamada manual de
startUpWebView, mova a inicialização destartUpWebViewpara antes no app ou configure o SDK ofensivo para atrasar as tarefas dependentes da WebView.
Migrar de soluções alternativas anteriores
No passado, talvez você tenha usado soluções alternativas explícitas para forçar a inicialização da WebView em uma linha de execução em segundo plano, como buscar a string do user agent.
Esses workarounds são considerados práticas sem suporte, e o comportamento
subjacente pode mudar nas próximas versões. Se o app depender de soluções alternativas explícitas e
não documentadas para acionar ou gerenciar a inicialização do WebView, recomendamos
usar a API startUpWebView. A API startUpWebView funciona em todas as versões do Android e do WebView compatíveis com a biblioteca Jetpack Webkit.
Usar a implementação do Jetpack Webkit ajuda a garantir um comportamento consistente em todo o ecossistema Android. Uma das principais vantagens dessa API é a resiliência: em dispositivos legados em que as otimizações mais recentes não estão disponíveis, a API mantém a paridade de desempenho com soluções manuais. Isso permite que você adote benefícios modernos de inicialização em dispositivos mais novos sem incorrer em uma penalidade de desempenho em dispositivos mais antigos.
Se você encontrar problemas ou tiver feedback sobre a API startUpWebView, informe um
bug no Issue Tracker público.