O WebViewCompat.navigate é uma alternativa aprimorada ao
WebView.loadUrl que oferece controle refinado sobre o carregamento de páginas da Web,
gerenciamento de histórico e rastreamento do ciclo de vida de navegação no WebView.
Antes, iniciar navegações de página usando loadUrl tinha limitações significativas:
- Nenhuma substituição de entrada do histórico:não é possível substituir a entrada atual do histórico, o que impede a navegação para uma nova página sem adicionar uma entrada à pilha de retorno.
- Callbacks dissociados:não havia um mecanismo direto para correlacionar uma chamada
loadUrlespecífica com eventos de callback subsequentes emWebViewClient. - Cabeçalhos extras não salvos:os cabeçalhos personalizados transmitidos para
loadUrlnão foram salvos como parte do estadoWebView. Portanto, eles foram perdidos ao restaurar o estado.
A API WebViewCompat.navigate resolve esses problemas com os seguintes recursos:
- Substituição de entrada do histórico de navegação:permite substituir a página atual na pilha de histórico
WebView. - Rastreamento de callback correlacionado:retorna um objeto
Navigationque serve como um identificador exclusivo em todas as etapas de um ciclo de vida de navegação. - Suporte a cabeçalho de estado salvo:cabeçalhos extras são salvos de maneira confiável no
pacote de estado
WebViewpara que possam ser reutilizados na restauração do estado.
Principais recursos e limitações
Antes de adotar o WebViewCompat.navigate, considere as seguintes regras e restrições operacionais:
Segurança de linhas de execução:invoque
WebViewCompat.navigatena linha de execução da interface (principal).Cancelamento e precedência:não é possível cancelar explicitamente as navegações em andamento. No entanto, iniciar uma nova chamada
navigateno mesmoWebViewsubstitui qualquer navegação ativa.Compatibilidade com esquemas de URI:os esquemas de URI padrão (como
https:ehttp:) e personalizados são compatíveis. O esquemajavascript:não é compatível.Limite de tamanho do URL:o comprimento máximo da string de URL aceito é de 2 MB.
Verificação de recursos:sempre verifique a disponibilidade de recursos usando
WebViewFeature.isFeatureSupportedantes de invocar a API para manter a compatibilidade entre diferentes versões do APK do WebView.
Iniciar a navegação e rastrear o ciclo de vida
Para configurar a navegação e acompanhar o ciclo de vida dela, faça o seguinte:
- Registre uma implementação de
NavigationListenerusandoWebViewCompat.addNavigationListenerdurante a configuração doWebViewpara receber callbacks estruturados do ciclo de vida. Registre o listener uma vez (em vez de em cada chamada de navegação) para evitar vazamentos de memória e execuções duplicadas de callback. - Construa uma instância
NavigationParametersusandoNavigationParameters.Builderpara especificar comportamentos opcionais, como substituição de histórico ou cabeçalhos HTTP personalizados. - Chame
WebViewCompat.navigate, transmitindo sua instânciaWebView, o URL de destino e os parâmetros.
WebViewCompat.navigate retorna um objeto Navigation que identifica a solicitação de forma exclusiva. Nos callbacks NavigationListener, compare esse objeto com o parâmetro Navigation recebido para rastrear essa navegação específica.
Exemplo de implementação
O exemplo a seguir demonstra como configurar parâmetros de navegação, invocar WebViewCompat.navigate e detectar eventos do ciclo de vida de navegação:
Kotlin
class WebNavigationManager(private val webView: WebView) {
// Track the navigation instance returned by the API
private var currentNavigation: Navigation? = null
init {
// 1. Define listener to observe navigation lifecycle events
val listener = object : NavigationListener {
override fun onNavigationStarted(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation started
}
}
override fun onNavigationRedirected(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation encountered a redirect
}
}
override fun onNavigationCompleted(navigation: Navigation) {
if (navigation == currentNavigation) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
val statusCode = navigation.statusCode
val error = navigation.webResourceError
}
}
}
override fun onFirstContentfulPaintMillis(page: Page, durationMillis: Long) {
// Match page with current navigation
if (page == currentNavigation?.page) {
// Page rendering started (First Contentful Paint achieved)
}
}
}
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(webView, listener)
}
@UiThread
fun navigateToPage(url: String) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
webView.loadUrl(url)
return
}
// 3. Configure navigation parameters
val params = NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(
mapOf("X-Test-Navigate-Header" to "TestValue")
)
.build()
// 4. Initiate navigation on the UI thread
currentNavigation = WebViewCompat.navigate(webView, url, params)
}
}
Java
public class WebNavigationManager {
private Navigation mCurrentNavigation;
private final WebView mWebView;
public WebNavigationManager(@NonNull WebView webView) {
mWebView = webView;
setupListener();
}
private void setupListener() {
// 1. Define listener to observe navigation lifecycle events
NavigationListener listener = new NavigationListener() {
@Override
public void onNavigationStarted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation started
}
}
@Override
public void onNavigationRedirected(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation encountered a redirect
}
}
@Override
public void onNavigationCompleted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
int statusCode = navigation.getStatusCode();
WebResourceErrorCompat error = navigation.getWebResourceError();
}
}
}
@Override
public void onFirstContentfulPaintMillis(@NonNull Page page, long durationMillis) {
if (mCurrentNavigation != null && page.equals(mCurrentNavigation.getPage())) {
// Page rendering started (First Contentful Paint achieved)
}
}
};
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(mWebView, listener);
}
@UiThread
public void navigateToPage(@NonNull String url) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
mWebView.loadUrl(url);
return;
}
// 3. Configure navigation parameters
NavigationParameters params = new NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(Collections.singletonMap(
"X-Test-Navigate-Header", "TestValue"
))
.build();
// 4. Initiate navigation on the UI thread
mCurrentNavigation = WebViewCompat.navigate(mWebView, url, params);
}
}
Propagar o estado do app usando cabeçalhos HTTP
Os apps da Web geralmente exigem contexto do app Android host para coordenar a lógica de back-end ou personalizar o conteúdo da Web. Adicionar parâmetros de consulta ao URL para transmitir essas informações pode prejudicar a aparência dos URLs, interferir no armazenamento em cache e expor o estado interno do app.
Em vez disso, recomendamos transmitir o contexto do app usando cabeçalhos HTTP personalizados. Ao usar
WebViewCompat.navigate e NavigationParameters, você pode enviar esses
dados com segurança ao seu servidor. Além disso, a WebView preserva esses cabeçalhos durante a restauração do estado, o que garante que o conteúdo da Web permaneça consistente em todas as mudanças de configuração. Essa persistência só se aplica ao usar
WebViewCompat.navigate. Se você usa WebView.loadUrl, os cabeçalhos personalizados não são
salvos no pacote de estado WebView e são perdidos na restauração.
Casos de uso comuns
Os casos de uso comuns para transmitir o contexto do app host incluem:
- Versão do app (
X-App-Version): transmitir a versão de lançamento do app host (comoBuildConfig.VERSION_NAME) ajuda o servidor de back-end a verificar a compatibilidade da ponte JavaScript nativa, restringir recursos ou pedir aos usuários para atualizar apps mais antigos. - Plataforma do cliente (
X-Client-Platform): ao identificar explicitamente o ambiente host como Android, o servidor pode fornecer uma interface adaptada à plataforma ou links de armazenamento de rotas sem depender da análise da stringUser-Agent.
Exemplo de implementação
O exemplo a seguir demonstra como transmitir a versão do aplicativo e a plataforma do cliente para um servidor da Web:
Kotlin
// Attach host app metadata so the server can verify compatibility and tailor content
val params = NavigationParameters.Builder()
.addAdditionalHeaders(
mapOf(
"X-App-Version" to BuildConfig.VERSION_NAME,
"X-Client-Platform" to "Android"
)
)
.build()
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params)
Java
// Attach host app metadata so the server can verify compatibility and tailor content
Map<String, String> headers = new HashMap<>();
headers.put("X-App-Version", BuildConfig.VERSION_NAME);
headers.put("X-Client-Platform", "Android");
NavigationParameters params = new NavigationParameters.Builder()
.addAdditionalHeaders(headers)
.build();
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params);
Modos de falha e tratamento de erros
A API WebViewCompat.navigate oferece mecanismos distintos para lidar com erros de configuração e falhas de navegação em tempo de execução:
Exceções de argumento inválido
A transmissão de argumentos inválidos aciona um IllegalArgumentException síncrono.
Estas são algumas causas comuns:
- Transmitir
nullpara parâmetros obrigatórios não nulos (webView,urlouparams). - Fornecer um esquema de URL sem suporte, como
javascript:. - Transmitir chaves ou valores de cabeçalho HTTP malformados que não estão em conformidade com as especificações da RFC 2616.
Erros no processo de navegação
Se ocorrer uma falha durante a solicitação de rede ou o carregamento de página (como um código de status HTTP 404, falha na resolução de DNS ou erro de SSL), WebViewCompat.navigate ainda vai retornar um objeto Navigation válido.
Quando a navegação terminar, inspecione os seguintes métodos na instância Navigation
dentro do callback onNavigationCompleted para diagnosticar a
falha:
getStatusCode: retorna o código de status da resposta HTTP (por exemplo,404ou500).getWebResourceError: retorna um objetoWebResourceErrorCompatque detalha erros de rede, como tempos limite de conexão ou falhas de pesquisa de host.didCommitErrorPage: indica se oWebViewconfirmou e mostrou uma página de erro ao usuário.didCommit: indica se a navegação foi confirmada com sucesso em uma página de destino sem ser interrompida.
Gerenciamento de pacotes de estado de salvamento
Quando você transmite cabeçalhos extras com NavigationParameters, WebView salva esses cabeçalhos no pacote de estado salvo para que possam ser reutilizados na restauração do estado. No entanto, grandes coleções de cabeçalhos podem aumentar substancialmente o tamanho do estado salvo Bundle.
Se você precisar restringir o tamanho do pacote para evitar TransactionTooLargeException
durante salvamentos de estado do Android, use WebViewCompat.saveState. Esse método
permite definir um limite máximo de tamanho do pacote em bytes e, opcionalmente, excluir itens do
histórico de encaminhamento:
Kotlin
// Save state with a maximum bundle size limit (for example, 64 KB)
val maxSizeBytes = 64 * 1024
val includeForwardState = false
val outState = Bundle()
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState)
Java
// Save state with a maximum bundle size limit (for example, 64 KB)
int maxSizeBytes = 64 * 1024;
boolean includeForwardState = false;
Bundle outState = new Bundle();
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState);
O pacote resultante continua compatível com o método padrão
WebView.restoreState.
Recomendações de migração e implementação
Para garantir a performance e a estabilidade ideais ao navegar no WebView,
siga estas recomendações:
Migrar de
loadUrlparanavigate:migre todas as chamadas legadas deWebView.loadUrlparaWebViewCompat.navigate. Isso garante um gerenciamento uniforme do histórico e que os cabeçalhos sejam sempre salvos como parte do estado salvo.Sempre verifique a compatibilidade de recursos:antes de invocar a API, confirme o suporte de tempo de execução com
WebViewFeature.isFeatureSupportedpara proteger contra versões mais antigas do WebView.Correlacionar instâncias de navegação:use o objeto
Navigationretornado para diferenciar navegações simultâneas ou filtrar callbacks ao gerenciar várias instâncias deWebView.Registre o listener uma vez durante a inicialização:como
WebViewCompat.addNavigationListeneradiciona um listener em vez de substituir um existente, registre seuNavigationListeneruma vez durante a configuração doWebViewpara evitar vazamentos de memória e execuções de callback duplicadas em navegações subsequentes.Monitore o tamanho do estado de salvamento:ao transmitir payloads de cabeçalho grandes, use
WebViewCompat.saveStatecom limites de tamanho explícitos para evitar salvar dados de estado excessivos.
Outros recursos
Para saber mais sobre recursos da Web incorporados e otimização de performance, consulte os seguintes guias:
- Simplifique sua implementação do WebView com o Jetpack Webkit
- Carregamento especulativo no WebView
- Otimizar a inicialização do WebView
- Processar o encerramento do processo de renderização do WebView