Quando o app usa um WebView pela primeira vez, o sistema realiza tarefas de inicialização específicas.
Esse processo é pesado. Por padrão, ele acontece implicitamente na linha de execução da interface pela primeira vez que o aplicativo chama muitas APIs nos pacotes android.webkit ou androidx.webkit ou infla um layout que contém uma tag WebView.
Por que isso é importante
Como essa inicialização implícita acontece totalmente na linha de execução principal, ela impede que o app processe a entrada do usuário e aumenta drasticamente o risco de erros de "O aplicativo 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 dos processos e linhas de execução overview.
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: chamando
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 o app, use startUpWebView em vez disso.
Esta página discute como otimizar a performance de inicialização do WebView usando a API startUpWebView.
Assuma o controle da inicialização do WebView
Para melhorar a performance e minimizar os ANRs, use a API startUpWebView disponível na biblioteca Jetpack Webkit. Essa API oferece controle explícito sobre quando o WebView é iniciado. Ela 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 da interface seja feito em partes, em vez de um grande bloco monolítico. Isso libera a linha de execução 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.
Use a versão 1.16.0 ou mais recente:
dependencies {
implementation("androidx.webkit:webkit:1.16.0")
}
Usar a API startUpWebView
A forma de otimizar o fluxo de inicialização depende de quando o app realmente precisa mostrar o WebView.
Quando o WebView não está no caminho crítico
Se o app não precisar carregar um WebView imediatamente, você poderá ocultar o custo de inicialização completamente. Chame startUpWebView no início do ciclo de vida do app e aguarde o callback de sucesso ser acionado.
O ideal é aguardar o callback antes de chamar outras APIs do WebView. Se você acionar startUpWebView, mas não esperar que ele termine antes de tocar em outros componentes do WebView, o sistema vai bloquear a linha de execução da interface enquanto aguarda a conclusão da inicialização. O app pode ter algum benefício de performance do trabalho em segundo plano já concluído, mas não o máximo.
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, você
ainda deve 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 do WebView diretamente quando elas forem necessárias.
Para aproveitar ao máximo a inicialização assíncrona, adie a instanciação de um WebView ou a chamada de APIs do WebView até que não haja mais operações de linha de execução da interface no caminho crítico a serem executadas (como inflar hierarquias de layout, inicializar outros SDKs ou desenhar o frame inicial).
Se você chamar startUpWebView e invocar imediatamente as APIs do WebView na linha de execução principal, a linha de execução da interface será bloqueada aguardando a inicialização. Nesse cenário, não há benefício de performance.
Se o uso do WebView puder se tornar crítico, mas você não quiser iniciar o WebView completamente, 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. Se você vai aguardar o callback nesse momento depende 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 as inicializações bem-sucedidas ou processe falhas de diagnóstico.
É seguro chamar startUpWebView várias vezes de diferentes partes do app. Recomendamos evitar a implementação de um loop de repetição 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 performance esperados, geralmente é porque o WebView está sendo inicializado implicitamente em outro lugar do app antes da execução da chamada. Isso pode ser devido aos seguintes motivos:
Bibliotecas de terceiros ou SDKs inicializados no início do ciclo de vida do app.
ContentProvidersinjetados no APK que acionam APIs do 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 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 principal da interface.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 rastreamento de pilha que pode ser registrado ou inspecionado para encontrar a classe e o método exatos que acionaram a inicialização.
Exemplo de implementação
Você pode 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 do WebView do app, use as seguintes estratégias para analisar os dados de diagnóstico e resolver gargalos de performance:
Procure rastreamentos de pilha inesperados:se
getUiThreadBlockingStartUpLocations()não estiver vazio, examine os rastreamentos de pilha impressos. Se você encontrar classes pertencentes a SDKs de terceiros ou componentes inesperados, terá encontrado um gargalo de inicialização implícito.Verifique a ordem das chamadas:se as saídas de registro mostrarem que uma inicialização implícita ocorreu antes da chamada manual
startUpWebView, mova a inicializaçãostartUpWebViewpara mais cedo no app ou configure o SDK ofensivo para atrasar as tarefas dependentes do WebView.
Migrar de soluções alternativas anteriores
No passado, você pode ter usado soluções alternativas explícitas para forçar a inicialização do WebView em uma linha de execução em segundo plano, como buscar a string do user agent.
Essas soluções alternativas são consideradas práticas não compatíveis, e o comportamento subjacente delas pode mudar em versões futuras. Se o app depende 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.
Garantir a resiliência e a estabilidade do app
O uso da implementação do Jetpack Webkit ajuda a garantir um comportamento consistente em todo o ecossistema Android. Uma vantagem fundamental dessa API é a resiliência: em dispositivos legados em que otimizações mais recentes não estão disponíveis, a API mantém a paridade de performance com soluções alternativas manuais. Isso permite que você adote benefícios de inicialização modernos em dispositivos mais recentes sem incorrer em uma penalidade de performance em dispositivos mais antigos.
Embora a otimização da inicialização do WebView reduza o risco de erros de ANR durante a inicialização do app, também é necessário proteger o app contra falhas do renderizador de execução e recuperação de memória do sistema. Para manter a estabilidade abrangente do app depois que o WebView estiver em execução, consulte Processar o encerramento do WebView.
Se você encontrar problemas ou tiver feedback sobre a API startUpWebView, registre um
bug no Issue Tracker público.