WebViewCompat.navigate to ulepszona alternatywa dla
WebView.loadUrl, która zapewnia szczegółową kontrolę nad wczytywaniem stron internetowych, zarządzaniem historią i śledzeniem cyklu życia nawigacji w WebView.
Wcześniej inicjowanie nawigacji po stronach za pomocą loadUrl miało istotne ograniczenia:
- Brak zastępowania wpisów w historii: nie można było zastąpić bieżącego wpisu w historii, co uniemożliwiało przejście do nowej strony bez dodawania wpisu do stosu wstecz.
- Rozdzielone wywołania zwrotne: nie było bezpośredniego mechanizmu, który umożliwiałby powiązanie konkretnego wywołania
loadUrlz kolejnymi zdarzeniami wywołania zwrotnego wWebViewClient. - Dodatkowe nagłówki nie są zapisywane: niestandardowe nagłówki przekazywane do
loadUrlnie były zapisywane jako część stanuWebView, więc były tracone podczas przywracania stanu.
Interfejs API WebViewCompat.navigate rozwiązuje te problemy, wprowadzając te funkcje:
- Zastępowanie wpisów w historii nawigacji: umożliwia zastąpienie bieżącej strony w stosie historii
WebView. - Powiązane śledzenie wywołań zwrotnych: zwraca obiekt
Navigation, który służy jako unikalny identyfikator na wszystkich etapach cyklu życia nawigacji. - Obsługa nagłówków zapisanych stanów: dodatkowe nagłówki są niezawodnie zapisywane w pakiecie stanu
WebView, dzięki czemu można ich ponownie użyć po przywróceniu stanu.
Kluczowe możliwości i ograniczenia
Zanim zaczniesz korzystać z WebViewCompat.navigate, zapoznaj się z tymi regułami i ograniczeniami operacyjnymi:
Bezpieczeństwo wątków: musisz wywołać
WebViewCompat.navigatew wątku interfejsu (głównym).Anulowanie i pierwszeństwo: nawigacji w toku nie można wyraźnie anulować. Jednak zainicjowanie nowego wywołania
navigatew tym samymWebViewzastępuje każdą aktywną nawigację.Obsługa schematów URI: obsługiwane są standardowe (np.
https:ihttp:) oraz niestandardowe schematy URI. Schematjavascript:nie jest obsługiwany.Limit rozmiaru adresu URL: maksymalna obsługiwana długość ciągu adresu URL to 2 MB.
Sprawdzanie funkcji: przed wywołaniem interfejsu API zawsze sprawdzaj dostępność funkcji za pomocą
WebViewFeature.isFeatureSupported, aby zachować zgodność z różnymi wersjami APK WebView.
Inicjowanie nawigacji i śledzenie cyklu życia
Aby skonfigurować nawigację i śledzić jej cykl życia:
- Zarejestruj implementację
NavigationListenerza pomocąWebViewCompat.addNavigationListenerpodczas konfiguracjiWebView, aby otrzymywać uporządkowane wywołania zwrotne cyklu życia. Zarejestruj detektor raz (a nie przy każdym wywołaniu nawigacji), aby zapobiec wyciekom pamięci i duplikowaniu wywołań zwrotnych. - Utwórz instancję
NavigationParametersza pomocąNavigationParameters.Builder, aby określić opcjonalne zachowania, takie jak zastępowanie historii lub niestandardowe nagłówki HTTP. - Wywołaj
WebViewCompat.navigate, przekazując instancjęWebView, docelowy adres URL i parametry.
WebViewCompat.navigate zwraca obiekt Navigation, który jednoznacznie
identyfikuje żądanie. W wywołaniach zwrotnych NavigationListener porównaj
ten obiekt z przychodzącym parametrem Navigation, aby śledzić konkretną
nawigację.
Przykład wdrożenia
Ten przykład pokazuje, jak skonfigurować parametry nawigacji, wywołać WebViewCompat.navigate i nasłuchiwać zdarzeń cyklu życia nawigacji:
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);
}
}
Rodzaje błędów i obsługa błędów
Interfejs API WebViewCompat.navigate udostępnia odrębne mechanizmy obsługi błędów konfiguracji i błędów nawigacji w czasie działania:
Wyjątki nieprawidłowego argumentu
Przekazanie nieprawidłowych argumentów powoduje synchroniczne wywołanie IllegalArgumentException.
Najczęstsze przyczyny:
- Przekazanie wartości
nulldla wymaganych parametrów, które nie mogą mieć wartości null (webView,urllubparams). - Podanie nieobsługiwanego schematu URI adresu URL, np.
javascript:. - Przekazanie nieprawidłowo sformatowanych kluczy lub wartości nagłówków HTTP, które nie są zgodne ze specyfikacjami RFC 2616.
Błędy procesu nawigacji
Jeśli podczas żądania sieciowego lub wczytywania strony wystąpi błąd (np. kod stanu HTTP 404, błąd rozpoznawania nazw DNS lub błąd protokołu SSL), WebViewCompat.navigate nadal zwraca prawidłowy obiekt Navigation.
Gdy nawigacja się zakończy, sprawdź te metody w Navigation
instancji w wywołaniu zwrotnym onNavigationCompleted, aby zdiagnozować
błąd:
getStatusCode: zwraca kod stanu odpowiedzi HTTP (np.404lub500).getWebResourceError: zwraca obiektWebResourceErrorCompatzawierający szczegółowe informacje o błędach sieciowych, takich jak przekroczenie limitu czasu połączenia lub błędy wyszukiwania hosta.didCommitErrorPage: wskazuje, czyWebViewzatwierdził i wyświetlił użytkownikowi stronę błędu.didCommit: wskazuje, czy nawigacja została pomyślnie zatwierdzona na stronie docelowej bez przerwania.
Zarządzanie pakietem stanu zapisu
Gdy przekazujesz dodatkowe nagłówki za pomocą NavigationParameters, WebView zapisuje te nagłówki w pakiecie stanu zapisu, aby można było ich ponownie użyć po przywróceniu stanu. Jednak duże zbiory nagłówków mogą znacznie zwiększyć rozmiar zapisanego stanu Bundle.
Jeśli musisz ograniczyć rozmiar pakietu, aby zapobiec wystąpieniu TransactionTooLargeException
podczas zapisywania stanu Androida, użyj WebViewCompat.saveState. Ta metoda umożliwia ustawienie maksymalnego limitu rozmiaru pakietu w bajtach i opcjonalne wykluczenie elementów historii do przodu:
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);
Wynikowy pakiet pozostaje zgodny ze standardową
WebView.restoreState metodą.
Zalecenia dotyczące migracji i implementacji
Aby zapewnić optymalną wydajność i stabilność podczas nawigacji w WebView, postępuj zgodnie z tymi zaleceniami:
Migracja z
loadUrldonavigate: przeprowadź migrację wszystkich starszychWebView.loadUrlwywołań doWebViewCompat.navigate. Zapewnia to jednolite zarządzanie historią i gwarantuje, że nagłówki są zawsze zapisywane jako część zapisanego stanu.Zawsze sprawdzaj obsługę funkcji: przed wywołaniem interfejsu API sprawdź obsługę w czasie działania za pomocą
WebViewFeature.isFeatureSupported, aby zabezpieczyć się przed starszymi wersjami WebView.Korelacja instancji nawigacji: użyj zwróconego obiektu
Navigation, aby odróżnić równoczesne nawigacje lub filtrować wywołania zwrotne podczas zarządzania wieloma instancjamiWebView.Zarejestruj detektor raz podczas inicjowania: ponieważ
WebViewCompat.addNavigationListenerdodaje detektor, a nie zastępuje istniejący, zarejestrujNavigationListenerraz podczasWebViewkonfiguracji, aby uniknąć wycieków pamięci i duplikowania wywołań zwrotnych w kolejnych nawigacjach.Monitoruj rozmiar stanu zapisu: podczas przekazywania dużych ładunków nagłówków używaj
WebViewCompat.saveStatez jawnymi granicami rozmiaru, aby uniknąć zapisywania nadmiernej ilości danych stanu.
Dodatkowe materiały
Więcej informacji o wbudowanych funkcjach internetowych i optymalizacji wydajności znajdziesz w tych przewodnikach:
- Uproszczenie implementacji WebView za pomocą Jetpack Webkit
- Ładowanie spekulacyjne w WebView
- Optymalizacja uruchamiania WebView
- Obsługa zakończenia procesu renderowania WebView