A latência de navegação é uma métrica essencial para a experiência do usuário. Para ajudar os desenvolvedores a reduzir essa latência, o WebView oferece APIs para carregamento especulativo e otimização de conexão. Assim, o app pode buscar ou renderizar conteúdo antes que o usuário navegue explicitamente até ele.
A WebView oferece suporte a três tipos principais de carregamento especulativo: pré-conexão, pré-busca e pré-renderização, além de dicas do QUIC para otimizar a negociação de protocolo.
Ao implementar uma estratégia de carregamento especulativo, é possível:
- Redução significativa na latência de carregamento de conteúdo da Web:mova o horário de início da rede para mais cedo no ciclo de vida do app e prefira protocolos mais rápidos, como HTTP/3.
- Taxas de sucesso de navegação mais altas:ao pré-aquecer a rede e o cache, é menos provável que as navegações falhem devido a problemas de rede temporários.
- Melhor capacidade de resposta percebida:a pré-renderização, em particular, permite transições instantâneas que fazem o app parecer muito mais rápido.
Escolher uma estratégia de carregamento especulativo
O principal diferencial entre essas estratégias está no escopo: as APIs de dicas de pré-conexão e QUIC são baseadas na origem, o que significa que elas só exigem o domínio de destino. As APIs de pré-busca e pré-renderização são baseadas em URL, o que significa que elas exigem o caminho exato da página da Web.
Como as dicas de pré-conexão e QUIC operam no nível da origem, é possível iniciá-las muito antes no ciclo de vida do app, mesmo antes de saber o conteúdo ou a página específica para que o usuário vai navegar.
A tabela a seguir compara essas três estratégias para ajudar você a escolher a certa para seu caso de uso:
| Recurso | Pré-conexão | Pré-busca | Pré-renderização |
|---|---|---|---|
| Meta principal | Aquecer a conexão | Fazer cache apenas de HTML (sem JavaScript ou CSS) | Pré-renderizar a página inteira |
| Scope | Nível de perfil (compartilhado entre WebViews) | Nível de perfil (compartilhado entre WebViews) | Nível do WebView (vinculado a um WebView específico) |
| API Jetpack WebKit | androidx.webkit.Profile |
androidx.webkit.Profile |
androidx.webkit.WebViewCompat |
| Métodos principais da API | preconnect(...) |
prefetchUrlAsync(...) |
prerenderUrlAsync(...) |
| Configuração | N/A | PrefetchCache.setMaxPrefetches()PrefetchCache.setPrefetchTtlSeconds() |
setMaxPrerenders() |
| Uso de recursos | Baixa (rede) | Médio (rede, memória) | Alto (CPU, memória, rede) |
| Quando usar | Quando a origem de destino é conhecida, mas o URL específico ainda não foi determinado. | Quando o URL exato é conhecido e a navegação é provável, com o cache compartilhado entre WebViews. | Quando o URL exato é conhecido e a navegação é altamente certa em um WebView específico. |
| Benefícios | Configuração de conexão mais rápida para qualquer URL na origem | Carga de rede mais rápida para URLs correspondentes | Navegação instantânea de verdade após a ativação |
Pré-conexão com origens
A pré-conexão acelera os carregamentos futuros realizando buscas DNS e handshakes TCP/TLS ou QUIC de forma preventiva para uma origem especificada.
Ao contrário da pré-busca e da pré-renderização, que exigem um URL de destino exato, a pré-conexão é estritamente baseada na origem. Isso permite fazer a chamada de pré-conexão muito antes da pré-busca e da pré-renderização.
Essa estratégia de baixo recurso e no nível do perfil reduz a latência inicial para qualquer compartilhamento de WebView desse perfil, desde que a origem ainda não tenha sido visitada. A conexão permanece aberta por aproximadamente 30 segundos, beneficiando qualquer solicitação HTTP, navegação ou subrecurso entre origens subsequente ao eliminar a sobrecarga de handshake.
Implementação
Para iniciar uma pré-conexão, chame preconnect(String url) em uma instância de Profile. Essa API precisa ser chamada na linha de execução da interface e exige que o
WebViewFeature.PRECONNECT seja compatível.
A API opera na origem, mas, para conveniência, um URL completo pode ser fornecido (como https://www.example.com/index.html). Isso é tratado automaticamente como uma chamada à origem (por exemplo, https://www.example.com). Várias origens podem ser conectadas chamando essa API várias vezes.
Kotlin
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html")
// This initiates a connection to the origin https://www.example.com
}
Java
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html");
// This initiates a connection to the origin https://www.example.com
}
Indicar suporte ao protocolo QUIC com dicas de QUIC
O HTTP/3 (que é executado no protocolo de transporte QUIC) oferece melhorias significativas na latência em relação ao HTTP/2, incluindo handshakes de 0-RTT, resiliência de conexão aprimorada e eliminação do bloqueio head-of-line durante a perda de pacotes.
Por padrão, o WebView só tenta uma conexão QUIC se tiver uma indicação de que
a origem é compatível com QUIC, como um cabeçalho Alt-Svc ou um registro DNS HTTPS
de uma interação anterior. Sem esse conhecimento prévio, a WebView usa HTTP/2
ou HTTP/1.1 para a conexão inicial.
Chamar addQuicHints pré-preenche essas informações de suporte ao protocolo, permitindo que
a WebView se conecte usando QUIC imediatamente na primeira conexão com as
origens especificadas.
Pré-conexão com dicas do QUIC
Embora as dicas de pré-conexão e QUIC sejam otimizações no nível da origem em um
Profile, elas têm funções distintas e complementares:
preconnect:abre e mantém ativamente uma conexão de rede (pesquisa de DNS e handshake TCP/TLS ou QUIC) por aproximadamente 30 segundos. Como mantém conexões de rede ativas abertas, ele consome recursos de dispositivo e de rede e deve ser reservado para origens de destino de alta probabilidade.addQuicHints:não gera tráfego de rede imediato. Ele atualiza as propriedades do servidor na memória da pilha de redeProfilepara registrar o suporte ao protocolo. Como ele tem uma sobrecarga insignificante, você pode configurar dicas de QUIC durante a inicialização do app para todas as origens conhecidas compatíveis com HTTP/3.
Para um desempenho ideal, chame addQuicHints antes de chamar preconnect,
prefetchUrlAsync ou loadUrl. Isso garante que qualquer pré-conexão ou
solicitação de página subsequente negocie o HTTP/3 desde o início.
Implementação
Para configurar dicas de QUIC, chame addQuicHints(Set<String> urls) em uma instância Profile. É necessário chamar essa API na linha de execução de interface e verificar se o WebView
é compatível com o recurso WebViewFeature.ADD_QUIC_HINTS_V1.
Assim como preconnect, addQuicHints opera em origens, mas URLs completos podem ser fornecidos (como https://www.example.com/index.html) e são normalizados automaticamente para a origem (https://www.example.com).
O método é aditivo: chamá-lo várias vezes mescla as origens fornecidas em todo o Profile.
Kotlin
// Must be called on the @UiThread
@OptIn(Profile.ExperimentalAddQuicHints::class)
if (WebViewFeature.isFeatureSupported(WebViewFeature.ADD_QUIC_HINTS_V1)) {
val quicOrigins = setOf(
"https://www.example.com",
"https://api.example.com"
)
profile.addQuicHints(quicOrigins)
// WebView now prioritizes HTTP/3 over QUIC for connections to these origins
}
Java
// Must be called on the @UiThread
// Requires @Profile.ExperimentalAddQuicHints annotation or suppression
if (WebViewFeature.isFeatureSupported(WebViewFeature.ADD_QUIC_HINTS_V1)) {
Set<String> quicOrigins = new HashSet<>(Arrays.asList(
"https://www.example.com",
"https://api.example.com"
));
profile.addQuicHints(quicOrigins);
// WebView now prioritizes HTTP/3 over QUIC for connections to these origins
}
Configuração comum: PrefetchParameters e PrerenderParameters
A pré-busca e a pré-renderização usam PrefetchParameters ou PrerenderParameters para personalizar a solicitação. Com essas classes, é possível fornecer
cabeçalhos e dicas adicionais para correspondência de URL, como configurações
No-Vary-Search.
Kotlin
// Isolated configuration specifically for Cache-Level Prefetching
val prefetchParams = PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Client", "Android-App-V2")
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, listOf("session_id", "click_ref"))
)
.build()
Java
PrefetchParameters prefetchParams = new PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Header", "value")
/**
* Hint to ignore specific query parameters during cache matching.
* This allows the cache to match even if the tracking_id differs.
*/
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, Arrays.asList("tracking_id"))
)
/**
* Determines if Client Hints are sent.
* NOTE: This is ignored for Prerendering API requests, which default to
* the WebView's WebSettings.getJavaScriptEnabled() value.
*/
.setJavaScriptEnabled(true)
.build();
Pré-busca de conteúdo
A pré-busca faz o download do recurso HTML principal de um URL e o armazena no cache de rede do perfil. No WebView, um Profile atua como um contêiner para dados do navegador, incluindo cookies, cache HTTP e service workers. Como a pré-busca é uma operação no nível do perfil, qualquer WebView associado a esse perfil pode aproveitar a resposta em cache.
Implementação
Para iniciar um pré-busca, chame prefetchUrlAsync() em uma instância Profile. Essa operação aceita apenas o esquema HTTPS.
Kotlin
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
object : WebViewOutcomeReceiver<PrefetchResult, PrefetchException> {
override fun onResult(result: PrefetchResult) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
override fun onError(error: PrefetchException) {
when (error) {
is PrefetchNetworkException -> {
// Isolates network layer or server-side HTTP anomalies
val code = error.httpStatusCode
// Facilitates rapid diagnosis of 4xx or 5xx server responses
}
else -> {
// Catches generalized execution failures and system constraints
}
}
}
}
)
Java
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
new WebViewOutcomeReceiver<PrefetchResult, PrefetchException>() {
@Override
public void onResult(PrefetchResult result) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
@Override
public void onError(PrefetchException error) {
if (error instanceof PrefetchNetworkException) {
// Isolates network layer or server-side HTTP anomalies
int code = ((PrefetchNetworkException) error).httpStatusCode;
// Facilitates rapid diagnosis of 4xx or 5xx server responses
} else {
// Catches generalized execution failures and system constraints
}
}
}
);
Ciclo de vida da interceptação
A solicitação de pré-busca da WebView muda quando e como o callback shouldInterceptRequest()
é acionado. Como isso tem um impacto direto no uso bem-sucedido do conteúdo pré-buscado, é fundamental entender o ciclo de vida de duas etapas:
1. A fase especulativa (solicitação de pré-busca)
Quando prefetchUrlAsync() é invocado, o WebView faz o download do recurso HTML principal
em segundo plano. shouldInterceptRequest() é completamente ignorado para esta solicitação em segundo plano. Qualquer lógica personalizada, tokens de autorização ou injeções de cabeçalho
normalmente processados no interceptor não são aplicados ao recurso HTML
pré-buscado.
2. A fase de navegação (ativação do usuário)
Quando o app navega explicitamente até o URL (por exemplo, usando
WebViewCompat.navigate ou loadUrl) ou o usuário clica em um link
correspondente, o WebView determina se pode usar o cache pré-buscado:
Avaliação principal de HTML:a WebView vai acionar
shouldInterceptRequest()para o HTML principal neste momento. Para veicular a página do cache de pré-busca com sucesso, seu interceptor precisa retornarnull. Se você retornar umWebResourceResponsepersonalizado, a WebView vai respeitar seu interceptador e ignorar completamente o cache de pré-busca.Avaliação de sub-recursos:depois que o HTML pré-buscado é liberado para uso, o
shouldInterceptRequest()é acionado normalmente para todos os sub-recursos subsequentes (como imagens, scripts e CSS) necessários para concluir a renderização da página.
Comportamentos principais
As seguintes características operacionais e verificações de qualificação regem como a WebView inicia e gerencia solicitações de pré-busca:
- Segurança de linhas de execução:as solicitações podem ser iniciadas em qualquer linha de execução.
- Qualificação:antes de iniciar uma busca, o WebView garante que a solicitação seja
segura e contextualmente adequada verificando o seguinte:
- Cookies atuais:para proteger a privacidade do usuário e evitar efeitos colaterais semelhantes a CSRF, a WebView pode ignorar a pré-busca se a solicitação exigir cookies autenticados específicos que possam acionar uma mudança de estado no servidor.
- Presença do service worker:se um service worker já estiver controlando o escopo do URL, a WebView poderá adiar o gerenciador de busca do service worker em vez de iniciar uma pré-busca de rede padrão.
- Disponibilidade de proxy:a WebView verifica se o caminho de rede atual (incluindo proxies configurados) está estável para evitar falhas em solicitações especulativas em configurações de rede complexas.
- Se uma pré-busca não for iniciada (mesmo com parâmetros válidos), geralmente é porque o WebView determinou que uma solicitação em segundo plano pode interferir na sessão atual ou no estado de segurança do usuário.
- Cancelamento:use
CancellationSignalpara encerrar uma solicitação em andamento e evitar que ela seja armazenada em cache.
Pré-renderização de páginas
A pré-renderização cria "conteúdos da Web" ocultos para renderizar totalmente uma página em segundo plano, incluindo a execução de scripts e a busca de sub-recursos. A pré-renderização depende da mesma infraestrutura subjacente da pré-busca. Se um app iniciar uma pré-renderização, a WebView primeiro vai fazer uma pré-busca da resposta para veicular a navegação de pré-renderização, evitando atividades de rede redundantes.
Implementação
A pré-renderização é uma operação no nível da instância do WebView. Chame prerenderUrlAsync() usando WebViewCompat da linha de execução de interface.
Kotlin
WebViewCompat.prerenderUrlAsync(
webView,
url,
cancellationSignal,
executor,
params,
object : PrerenderOperationCallback {
override fun onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in
}
override fun onError(exception: Throwable) {
// exception is an instance of PrerenderException
// Handle prerender failure (for example, memory pressure or disallowed JavaScript APIs)
}
}
)
Java
WebViewCompat.prerenderUrlAsync(webView, url, cancellationSignal, executor, params, new PrerenderOperationCallback() {
@Override
public void onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in.
}
@Override
public void onError(@NonNull Throwable exception) {
// Handle prerender failure (for example, resource constraints or disallowed APIs).
}
});
A pré-busca e a pré-renderização são totalmente assíncronas. prefetchUrlAsync() pode ser
chamado de qualquer linha de execução, enquanto prerenderUrlAsync() precisa ser iniciado na
linha de execução da interface.
Restrições técnicas
Para equilibrar a navegação instantânea com a integridade do sistema, a WebView impõe as seguintes restrições de tempo de execução:
- Pressão de memória:a WebView cancela URLs pré-renderizados se o dispositivo estiver com pouca RAM.
- APIs não permitidas:qualquer tentativa do JavaScript de acessar determinadas APIs (por exemplo, reprodução de áudio, alertas) em um contexto em segundo plano vai encerrar imediatamente a pré-renderização.
- Limite de instâncias:há um limite para o número de URLs pré-renderizados ativos permitidos por WebView.
Correspondência de URL e No-Vary-Search (NVS)
O WebView exige um algoritmo de correspondência confiável para garantir que um recurso pré-carregado seja fornecido apenas para a navegação pretendida.
Correspondência exata x correspondência de NVS
Por padrão, a pré-busca e a pré-renderização exigem uma correspondência exata de URL. Se o URL navegado for idêntico ao URL pré-carregado, ele será veiculado imediatamente do cache. Se os parâmetros de consulta forem diferentes, a WebView usará as seguintes regras de pesquisa sem variação (NVS, na sigla em inglês):
- A dica:os desenvolvedores fornecem uma dica
setExpectedNoVarySearchHeader()durante a iniciação. Se o URL navegado corresponder ao URL da solicitação menos os parâmetros sugeridos, a WebView será bloqueada brevemente para aguardar os cabeçalhos reais do servidor. - Cabeçalho do servidor:o cabeçalho de resposta do NVS do servidor é a autoridade máxima. Se o servidor confirmar que as diferenças de consulta devem ser ignoradas, a correspondência será veiculada do cache. Caso contrário, o WebView volta para um carregamento de rede frio.
O No-Vary-Search (NVS) é para uso avançado, e a maioria dos desenvolvedores não precisa dele porque transmite exatamente o mesmo URL para pré-busca e navegação (WebViewCompat.navigate ou loadUrl). Esta orientação só é necessária se houver diferenças nos parâmetros de consulta entre o URL de pré-busca e o URL navegado.
Configuração global
Ajuste o comportamento de carregamento especulativo no nível do perfil configurando limites de PrefetchCache e pré-renderizações máximas. Também é possível redefinir os limites personalizados de pré-busca para os padrões do sistema:
Kotlin
// Configure prefetch cache limits
profile.prefetchCache.setMaxPrefetches(10)
profile.prefetchCache.setPrefetchTtlSeconds(60)
// Reset to system defaults when needed
profile.prefetchCache.clearMaxPrefetches()
// Configure maximum active prerenders
profile.setMaxPrerenders(2)
Java
// Configure prefetch cache limits
PrefetchCache prefetchCache = profile.getPrefetchCache();
prefetchCache.setMaxPrefetches(10);
prefetchCache.setPrefetchTtlSeconds(60);
// Reset to system defaults when needed
prefetchCache.clearMaxPrefetches();
// Configure maximum active prerenders
profile.setMaxPrerenders(2);
Tratamento de erros e exceções
As operações especulativas usam um OutcomeReceiverCompat ou
PrerenderOperationCallback para informar os resultados.
Exceções principais
Quando uma operação de carregamento especulativo falha, o gerenciador de erros informa um dos seguintes tipos de exceção principais para ajudar a diagnosticar cenários de falha específicos:
PrefetchException: A classe de base para todos os erros de pré-busca assíncrona.PrefetchNetworkException:indica uma falha no nível da rede ou do servidor. Ele pode incluir um campohttpStatusCode(como 404 ou 503) para ajudar a diagnosticar problemas do lado do servidor.PrerenderException:a superclasse de todos os erros relacionados à pré-renderização, como falhas devido à pressão da memória ou ao uso de APIs não permitidas (como reprodução de áudio) em segundo plano.
Estratégias de otimização
Siga estas recomendações para maximizar os benefícios do carregamento especulativo e conservar os recursos do sistema:
- Inicie cedo:comece a pré-busca durante a inicialização do app ou assim que um destino de navegação for provável.
- Emparelhe dicas do QUIC com pré-aquecimento de conexão:chame
addQuicHints()antes de iniciarpreconnect(),prefetchUrlAsync()ou navegações padrão para garantir que a WebView tente estabelecer conexões usando HTTP/3. - Estratégia integrada:se você pré-renderizar um URL já no cache de pré-busca, a navegação de pré-renderização será veiculada desse cache, evitando solicitações de rede redundantes.
- Monitore as cotas:a pré-renderização consome muitos recursos. Prefira a pré-busca para vários candidatos prováveis e reserve a pré-renderização para a navegação única mais provável.
- Suporte a esquemas:verifique se todos os URLs usam o esquema HTTPS obrigatório. Esquemas inválidos ou entradas nulas acionam um
IllegalArgumentExceptionsíncrono.
Outros recursos
Para saber mais sobre como depurar apps da Web, otimizar o desempenho de inicialização da WebView e processar o encerramento do processo de renderização, consulte os seguintes recursos:
- Navegação aprimorada na página com o método
WebViewCompat.navigate - Depurar apps da Web
- Otimizar a inicialização do WebView
- Processar o encerramento do processo de renderização do WebView