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, permitindo que o app busque ou renderize conteúdo antes que o usuário navegue explicitamente até ele.
O WebView oferece suporte a três tipos principais de carregamento especulativo: pré-conexão, pré-busca e pré-renderização.
Ao implementar uma estratégia de carregamento especulativo, você pode conseguir o seguinte:
- 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.
- Taxas de sucesso de navegação mais altas:ao pré-aquecer a rede e o cache, as navegações têm menos probabilidade de falhar devido a problemas de rede temporários.
- Melhor capacidade de resposta percebida:a pré-renderização, em particular, permite transições instantâneas que tornam o app significativamente mais rápido.
Escolher uma estratégia de carregamento especulativo
O principal diferenciador entre essas estratégias está no escopo delas: a API Preconnect é baseada na origem, o que significa que ela só exige o domínio de destino. As APIs Prefetch e Prerender são baseadas em URL, o que significa que elas exigem o caminho exato da página da Web.
Como a pré-conexão opera no nível de origem, ela pode ser iniciada muito mais cedo no ciclo de vida do app, mesmo antes de você saber o conteúdo ou a página específica para a qual 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 |
|---|---|---|---|
| Objetivo principal | Aquecer a conexão | Armazenar em cache apenas 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 de 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 | Baixo (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 armazenamento em 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 | Carregamento de rede mais rápido para URLs correspondentes | Navegação realmente instantânea após a ativação |
Pré-conexão com origens
A pré-conexão acelera os carregamentos futuros, realizando pesquisas de DNS e handshakes TCP/TLS 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 que você faça a chamada de pré-conexão muito mais cedo do que a pré-busca e a pré-renderização.
Essa estratégia de baixo recurso e nível de perfil reduz a latência inicial de qualquer WebView que compartilhe esse perfil, desde que a origem ainda não tenha sido visitada. A conexão permanece aberta por aproximadamente 30 segundos, beneficiando todas as solicitações HTTP, navegações ou sub-recursos de origem cruzada subsequentes, eliminando a sobrecarga de handshake.
Implementação
Para iniciar uma pré-conexão, chame preconnect(String url) em uma instância 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 maior conveniência, um URL completo pode ser fornecido
(como https://www.example.com/index.html). Isso é tratado automaticamente como
uma chamada para a 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
}
Configuração comum: PrefetchParameters e PrerenderParameters
A pré-busca e a pré-renderização usam PrefetchParameters ou PrerenderParameters para personalizar a solicitação. Essas classes permitem 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 armazenada em cache.
Implementação
Para iniciar uma pré-busca, chame prefetchUrlAsync() em uma instância Profile. Essa operação só oferece suporte ao 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 de interceptação
A solicitação de pré-busca do WebView altera quando e como o callback shouldInterceptRequest() é acionado. Como isso tem um impacto direto sobre se o conteúdo pré-buscado é usado com sucesso, é 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 essa 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 para o URL (por exemplo, usando 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:o WebView vai acionar
shouldInterceptRequest()para o HTML principal neste momento. Para veicular a página do cache de pré-busca, o interceptor precisa retornarnull. Se você retornar umWebResourceResponsepersonalizado, o WebView vai respeitar o interceptor e ignorar completamente o cache de pré-busca.Avaliação de sub-recursos:depois que o HTML pré-buscado é liberado para uso,
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 o 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 apropriada, verificando o seguinte:
- Cookies atuais:para proteger a privacidade do usuário e evitar efeitos colaterais semelhantes a CSRF, o 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, o WebView poderá adiar para o handler de busca do service worker em vez de iniciar uma pré-busca de rede padrão.
- Disponibilidade de proxy:o 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 o
CancellationSignalpara encerrar uma solicitação em andamento e impedir 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, o WebView primeiro vai realizar 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 na linha de execução da 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, o WebView aplica as seguintes restrições de execução:
- Pressão de memória:o 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 veiculado apenas para a navegação pretendida.
Correspondência exata x correspondência 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 no cache. Se os parâmetros de consulta forem diferentes, o WebView vai usar as seguintes regras No-Vary-Search (NVS):
- A dica:os desenvolvedores fornecem uma dica
setExpectedNoVarySearchHeader()durante a inicialização. Se o URL navegado corresponder ao URL da solicitação menos os parâmetros sugeridos, o WebView vai bloquear brevemente para aguardar os cabeçalhos reais do servidor. - Cabeçalho do servidor:o cabeçalho de resposta NVS do servidor é a autoridade máxima. Se o servidor confirmar que as diferenças de consulta precisam ser ignoradas, a correspondência será veiculada no cache. Caso contrário, o WebView vai voltar para um carregamento de rede a frio.
O No-Vary-Search (NVS) é para uso avançado, e a maioria dos desenvolvedores não precisa dele porque transmite o mesmo URL exato para a pré-busca e loadUrl(). Essa orientação só é necessária se houver diferenças nos parâmetros de consulta entre o URL de pré-busca e o URL loadUrl().
Configuração global
Ajuste o comportamento de carregamento especulativo no nível do perfil configurando os limites de PrefetchCache e as pré-renderizações máximas. Você também pode redefinir os limites de pré-busca personalizados 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 handler 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íncronos.PrefetchNetworkException:indica uma falha de rede ou de servidor. Ele pode incluir um campohttpStatusCode(como 404 ou 503) para ajudar a diagnosticar problemas do lado do servidor.PrerenderException:a superclasse para todos os erros relacionados à pré-renderização, como falhas devido à pressão de 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, conservando os recursos do sistema:
- Iniciar cedo:comece a pré-busca durante a inicialização do app ou assim que um destino de navegação for provável.
- Estratégia integrada:se você pré-renderizar um URL que já está no cache de pré-busca, a navegação de pré-renderização será veiculada nesse cache, evitando solicitações de rede redundantes.
- Monitorar cotas:a pré-renderização exige muitos recursos. Prefira a pré-busca para vários candidatos prováveis e reserve a pré-renderização para a navegação mais provável.
- Suporte a esquemas:verifique se todos os URLs usam o esquema HTTPS obrigatório. Esquemas inválidos ou entradas nulas acionam uma
IllegalArgumentExceptionsíncrona.
Outros recursos
Para saber mais sobre como depurar apps da Web, otimizar a performance de inicialização do WebView e processar o encerramento do processo de renderização, consulte os seguintes recursos:
- Depurar apps da Web
- Otimizar a inicialização do WebView
- Processar o encerramento do processo de renderização do WebView