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 wpisu 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 wstecznego.
- Odłączone wywołania zwrotne: w
WebViewClientnie było bezpośredniego mechanizmu, który umożliwiałby powiązanie konkretnego wywołanialoadUrlz kolejnymi zdarzeniami wywołania zwrotnego. - Dodatkowe nagłówki nie zostały zapisane: niestandardowe nagłówki przekazane do
loadUrlnie zostały zapisane w ramach stanuWebView, więc zostały utracone podczas przywracania stanu.
Interfejs WebViewCompat.navigate API rozwiązuje te problemy, wprowadzając te funkcje:
- Zastępowanie wpisu w historii nawigacji: umożliwia zastąpienie bieżącej strony w
WebViewstosie historii. - Śledzenie powiązanych 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 stanu zapisanego: dodatkowe nagłówki są niezawodnie zapisywane w pakiecie stanu
WebView, dzięki czemu można ich używać ponownie po przywróceniu stanu.
Najważniejsze funkcje i ograniczenia
Zanim zaczniesz korzystać z WebViewCompat.navigate, weź pod uwagę te zasady i ograniczenia operacyjne:
Bezpieczeństwo wątków: musisz wywołać
WebViewCompat.navigatew wątku interfejsu (głównym).Anulowanie i priorytet: nawigacji w trakcie lotu nie można wyraźnie anulować. Jednak rozpoczęcie nowego połączenia
navigatena tym samym urządzeniuWebViewzastępuje aktywną nawigację.Obsługa schematów URI: obsługiwane są standardowe (np.
https:ihttp:) i 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ą metody
WebViewFeature.isFeatureSupported, aby zachować zgodność z różnymi wersjami pliku APK WebView.
Rozpoczynanie nawigacji i śledzenie cyklu życia
Aby skonfigurować nawigację i śledzić jej cykl życia:
- Zarejestruj implementację
NavigationListenerza pomocąWebViewCompat.addNavigationListenerpodczas konfiguracjiWebView, aby otrzymywać strukturalne wywołania zwrotne dotyczące cyklu życia. Zarejestruj detektor raz (zamiast przy każdym wywołaniu nawigacji), aby zapobiec wyciekom pamięci i wykonywaniu duplikatów wywołań zwrotnych. - Utwórz instancję
NavigationParametersza pomocą funkcjiNavigationParameters.Builder, aby określić opcjonalne zachowania, takie jak zastępowanie historii lub niestandardowe nagłówki HTTP. - Wywołaj funkcję
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 implementacji
W przykładzie poniżej pokazujemy, jak skonfigurować parametry nawigacji, wywołać funkcję 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);
}
}
Przekazywanie stanu aplikacji za pomocą nagłówków HTTP
Aplikacje internetowe często wymagają kontekstu z aplikacji na Androida, aby koordynować logikę backendu lub dostosowywać treści internetowe. Dołączanie parametrów zapytania do adresu URL w celu przekazywania tych informacji może powodować nieporządek w adresach URL, zakłócać buforowanie i ujawniać wewnętrzny stan aplikacji.
Zamiast tego zalecamy przekazywanie kontekstu aplikacji za pomocą niestandardowych nagłówków HTTP. Za pomocą funkcji WebViewCompat.navigate i NavigationParameters możesz bezpiecznie wysyłać te dane na swój serwer. Dodatkowo komponent WebView zachowuje te nagłówki podczas przywracania stanu, co zapewnia spójność treści internetowych w przypadku zmian konfiguracji. Pamiętaj, że ta trwałość ma zastosowanie tylko w przypadku korzystania z WebViewCompat.navigate. Jeśli używasz WebView.loadUrl, niestandardowe nagłówki nie są zapisywane w pakiecie stanu WebView i zostaną utracone po przywróceniu.
Częste przypadki użycia
Typowe przypadki przekazywania kontekstu aplikacji hosta to:
- Wersja aplikacji (
X-App-Version): Przekazywanie wersji do publikacji aplikacji hosta (np.BuildConfig.VERSION_NAME) pomaga serwerowi backendu w weryfikacji zgodności natywnego mostu JavaScript, ograniczaniu dostępu do funkcji lub wyświetlaniu użytkownikom prośby o zaktualizowanie starszych aplikacji. - Platforma klienta (
X-Client-Platform): wyraźne określenie środowiska hosta jako Androida umożliwia serwerowi dostarczanie interfejsu dostosowanego do platformy lub linków do sklepu z aplikacjami bez polegania na analizowaniu ciąguUser-Agent.
Przykład implementacji
Ten przykład pokazuje, jak przekazać wersję aplikacji i platformę klienta na serwer WWW:
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);
Rodzaje błędów i obsługa błędów
Interfejs WebViewCompat.navigate API 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 funkcji IllegalArgumentException.
Oto najczęstsze przyczyny:
- Przekazywanie wartości
nullw przypadku wymaganych parametrów, które nie mogą mieć wartości null (webView,urllubparams). - Podanie nieobsługiwanego schematu URI adresu URL, np.
javascript:. - Przekazywanie nieprawidłowych kluczy lub wartości nagłówka 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), funkcja WebViewCompat.navigate
zwróci prawidłowy obiekt Navigation.
Po zakończeniu nawigacji sprawdź te metody w instancji Navigation 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 sieci, takich jak przekroczenie limitu czasu połączenia lub nieudane wyszukiwanie hosta.didCommitErrorPage: wskazuje, czyWebViewpopełnił błąd i wyświetlił użytkownikowi stronę z komunikatem o błędzie.didCommit: wskazuje, czy nawigacja została pomyślnie przekierowana na stronę docelową bez przerwania.
Zarządzanie pakietem stanu zapisu
Gdy przekażesz dodatkowe nagłówki za pomocą NavigationParameters, WebView zapisze je w pakiecie stanu zapisanego, aby można było ich użyć ponownie podczas przywracania stanu. Jednak duże zbiory nagłówków mogą znacznie zwiększyć rozmiar zapisanego stanuBundle.
Jeśli chcesz ograniczyć rozmiar pakietu, aby zapobiec TransactionTooLargeException
podczas zapisywania stanu Androida, użyj WebViewCompat.saveState. Ta metoda pozwala ustawić maksymalny limit rozmiaru pakietu w bajtach i opcjonalnie wykluczyć elementy 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ą metodą WebView.restoreState.
Zalecenia dotyczące migracji i wdrażania
Aby zapewnić optymalną wydajność i stabilność podczas korzystania z WebView, postępuj zgodnie z tymi zaleceniami:
Migracja z
loadUrldonavigate: przenieś wszystkie połączenia starszego typuWebView.loadUrldoWebViewCompat.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 zdarzeń raz podczas inicjowania: ponieważ funkcja
WebViewCompat.addNavigationListenerdodaje detektor zdarzeń zamiast zastępować istniejący, zarejestruj funkcjęNavigationListenerraz podczasWebViewkonfiguracji, aby uniknąć wycieków pamięci i wykonywania duplikatów wywołań zwrotnych podczas kolejnych nawigacji.Monitoruj rozmiar stanu zapisu: podczas przekazywania dużych ładunków nagłówka używaj funkcji
WebViewCompat.saveStatez określonymi granicami rozmiaru, aby uniknąć zapisywania nadmiernych danych stanu.
Dodatkowe materiały
Więcej informacji o osadzonych uprawnieniach witryn i optymalizacji wydajności znajdziesz w tych przewodnikach:
- Uprość implementację WebView za pomocą Jetpack Webkit
- Ładowanie spekulacyjne w komponencie WebView
- Optymalizacja uruchamiania komponentu WebView
- Obsługa zakończenia procesu renderowania komponentu WebView