Ładowanie spekulacyjne w WebView

Opóźnienie nawigacji to kluczowy wskaźnik wrażeń użytkownika. Aby pomóc deweloperom zmniejszyć to opóźnienie, WebView udostępnia interfejsy API do ładowania spekulacyjnego i optymalizacji połączeń, dzięki czemu aplikacja może pobierać lub renderować treści, zanim użytkownik do nich przejdzie.

WebView obsługuje 3 główne typy spekulacyjnego wczytywania: wstępne nawiązywanie połączenia, wstępne pobieranie i wstępne renderowanie, a także wskazówki QUIC do optymalizacji negocjacji protokołu.

Wdrażając strategię ładowania spekulacyjnego, możesz osiągnąć następujące korzyści:

  • Znaczne skrócenie czasu oczekiwania na wczytanie treści internetowych: przenieś czas rozpoczęcia działania sieci na wcześniejszy etap cyklu życia aplikacji i używaj szybszych protokołów, takich jak HTTP/3.
  • Wyższy odsetek udanych nawigacji: dzięki wstępnemu rozgrzaniu sieci i pamięci podręcznej nawigacje rzadziej kończą się niepowodzeniem z powodu przejściowych problemów z siecią.
  • Lepsza postrzegana szybkość reakcji: renderowanie wstępne umożliwia natychmiastowe przejścia, dzięki czemu aplikacja wydaje się znacznie szybsza.

Wybieranie strategii ładowania spekulacyjnego

Główna różnica między tymi strategiami polega na ich zakresie: interfejsy API wskazówek dotyczących wstępnego nawiązywania połączenia i QUIC są oparte na źródle, co oznacza, że wymagają tylko domeny docelowej. Interfejsy API Prefetch i Prerender są oparte na adresach URL, co oznacza, że wymagają dokładnej ścieżki do strony.

Wskazówki dotyczące wstępnego nawiązywania połączenia i QUIC działają na poziomie źródła, więc możesz je inicjować znacznie wcześniej w cyklu życia aplikacji, nawet zanim poznasz konkretną treść lub stronę, do której przejdzie użytkownik.

Poniższa tabela porównuje te 3 strategie, aby pomóc Ci wybrać odpowiednią dla Twojego przypadku:

Funkcja Preconnect Pobieranie z wyprzedzeniem Prerender
Cel główny Rozgrzej połączenie Pamięć podręczna tylko dla HTML (bez JavaScriptu ani CSS) Wstępne renderowanie całej strony
Zakres Poziom profilu (udostępniany w widokach WebView) Poziom profilu (udostępniany w widokach WebView) Poziom WebView (powiązany z określonym widokiem WebView)
Jetpack WebKit API androidx.webkit.Profile androidx.webkit.Profile androidx.webkit.WebViewCompat
Podstawowe metody interfejsu API preconnect(...) prefetchUrlAsync(...) prerenderUrlAsync(...)
Konfiguracja Nie dotyczy PrefetchCache.setMaxPrefetches()
PrefetchCache.setPrefetchTtlSeconds()
setMaxPrerenders()
Wykorzystanie zasobów Niska (sieć) Średnie (sieć, pamięć) Wysokie (procesor, pamięć, sieć)
Kiedy używać Gdy znane jest źródło docelowe, ale konkretny adres URL nie został jeszcze określony. Gdy dokładny adres URL jest znany, a nawigacja jest prawdopodobna, z pamięcią podręczną udostępnianą w widokach WebView. Gdy dokładny adres URL jest znany, a nawigacja w określonym widoku WebView jest bardzo pewna.
Zalety Szybsze konfigurowanie połączenia z dowolnym adresem URL w domenie źródłowej Szybsze wczytywanie sieci w przypadku pasujących adresów URL Prawdziwie natychmiastowa nawigacja po aktywacji

Nawiązywanie połączeń ze źródłami w trybie preconnect

Wstępne łączenie przyspiesza przyszłe ładowanie, ponieważ wyprzedzająco wykonuje wyszukiwanie DNS i uzgadnianie połączenia TCP/TLS lub QUIC dla określonego źródła.

W przeciwieństwie do pobierania z wyprzedzeniem i wstępnego renderowania, które wymagają dokładnego docelowego adresu URL, wstępne łączenie jest oparte wyłącznie na źródle. Dzięki temu możesz wykonać połączenie wstępne znacznie wcześniej niż pobieranie wstępne i wstępne renderowanie.

Ta strategia na poziomie profilu, która nie wymaga dużych zasobów, zmniejsza początkowe opóźnienie w przypadku udostępniania dowolnego elementu WebView, który korzysta z tego profilu, pod warunkiem że źródło nie zostało jeszcze odwiedzone. Połączenie pozostaje otwarte przez około 30 sekund, co ułatwia kolejne żądania HTTP ze współdzieleniem zasobów z innych domen, nawigacje i zasoby podrzędne, ponieważ eliminuje narzut związany z uzgadnianiem połączenia.

Implementacja

Aby zainicjować wstępne połączenie, zadzwoń pod numer preconnect(String url) na instancji Profile. Ten interfejs API musi być wywoływany w wątku UI i wymaga obsługi WebViewFeature.PRECONNECT.

Interfejs API działa na podstawie źródła, ale dla wygody można podać pełny adres URL (np. https://www.example.com/index.html). Jest on automatycznie traktowany jako wywołanie źródła (np. https://www.example.com). Z wieloma źródłami można się połączyć, wywołując ten interfejs API kilka razy.

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
}

Wskazywanie obsługi protokołu QUIC za pomocą wskazówek QUIC

HTTP/3 (który działa w protokole transportowym QUIC) zapewnia znacznie mniejsze opóźnienia niż HTTP/2, w tym uzgadnianie połączenia 0-RTT, większą odporność połączenia i eliminację blokowania na początku kolejki w przypadku utraty pakietów.

Domyślnie WebView próbuje nawiązać połączenie QUIC tylko wtedy, gdy ma informację, że pochodzenie obsługuje QUIC, np. z nagłówka Alt-Svc lub rekordu DNS HTTPS z poprzedniej interakcji. Bez tej wiedzy WebView używa protokołu HTTP/2 lub HTTP/1.1 do nawiązania połączenia początkowego.

Wywołanie addQuicHints wstępnie wypełnia informacje o obsłudze tego protokołu, co umożliwia WebView natychmiastowe połączenie przy użyciu QUIC podczas pierwszego połączenia z określonymi źródłami.

Wcześniejsze łączenie z użyciem wskazówek QUIC

Wskazówki dotyczące wstępnego nawiązywania połączenia i QUIC to optymalizacje na poziomie źródła, ale pełnią różne, uzupełniające się role:Profile

  • preconnect: aktywnie otwiera i utrzymuje połączenie sieciowe (wyszukiwanie DNS i uzgadnianie połączenia TCP/TLS lub QUIC) przez około 30 sekund. Ponieważ utrzymuje otwarte aktywne połączenia sieciowe, zużywa zasoby urządzenia i sieci, dlatego powinna być zarezerwowana dla docelowych źródeł o wysokim prawdopodobieństwie.
  • addQuicHints: Nie powoduje natychmiastowego ruchu w sieci. Aktualizuje właściwości serwera w pamięci Profilestosu sieciowego, aby rejestrować obsługę protokołu. Ponieważ ma znikome obciążenie, możesz bezpiecznie konfigurować wskazówki QUIC podczas uruchamiania aplikacji dla wszystkich znanych źródeł obsługujących HTTP/3.

Aby uzyskać optymalną wydajność, wywołaj funkcję addQuicHints przed wywołaniem funkcji preconnect,prefetchUrlAsync lub loadUrl. Dzięki temu wszystkie kolejne połączenia wstępne lub żądania stron będą od początku negocjować protokół HTTP/3.

Implementacja

Aby skonfigurować wskazówki QUIC, wywołaj addQuicHints(Set<String> urls) na instancji Profile. Ten interfejs API musisz wywołać w wątku UI i sprawdzić, czy komponent WebView obsługuje funkcję WebViewFeature.ADD_QUIC_HINTS_V1.

Podobnie jak preconnect, addQuicHints działa na podstawie źródeł, ale można podać pełne adresy URL (np. https://www.example.com/index.html), które są automatycznie normalizowane do źródła (https://www.example.com).

Ta metoda jest addytywna: wywołanie jej kilka razy powoduje scalenie podanych domen w 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
}

Typowa konfiguracja: PrefetchParametersPrerenderParameters

Zarówno w przypadku wstępnego pobierania, jak i wstępnego renderowania używa się PrefetchParameters lub PrerenderParameters do dostosowywania żądania. Te klasy umożliwiają podawanie dodatkowych nagłówków i wskazówek dotyczących dopasowywania adresów URL, np. konfiguracji 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();

Pobieranie treści z wyprzedzeniem

Wstępne pobieranie polega na pobraniu głównego zasobu HTML adresu URL i zapisaniu go w pamięci podręcznej sieci profilu. W WebView Profile działa jako kontener danych przeglądarki, w tym plików cookie, pamięci podręcznej HTTP i service workerów. Prefetching to operacja na poziomie profilu, więc każdy WebView powiązany z tym profilem może korzystać z odpowiedzi zapisanej w pamięci podręcznej.

Implementacja

Aby zainicjować wstępne pobieranie, wywołaj metodę prefetchUrlAsync() w instancji Profile. Ta operacja obsługuje tylko schemat 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
            }
        }
    }
);

Cykl życia przechwytywania

Żądanie wstępnego pobierania WebView zmienia czas i sposób wywoływania shouldInterceptRequest() wywołania zwrotnego. Ma to bezpośredni wpływ na to, czy wstępnie pobrane treści zostaną użyte, dlatego ważne jest, aby poznać 2-etapowy cykl życia:

Diagram przedstawiający dwuetapowy cykl życia przechwytywania wstępnego pobierania elementu WebView podczas fazy spekulacyjnej i nawigacji.
Rysunek 1. Dwuetapowy cykl życia przechwytywania w przypadku żądań i nawigacji związanych z wstępnym pobieraniem WebView.

1. Faza spekulacyjna (żądanie pobierania z wyprzedzeniem)

Gdy wywoływana jest funkcja prefetchUrlAsync(), WebView pobiera w tle główny zasób HTML. W przypadku tego żądania w tle shouldInterceptRequest() jest całkowicie pomijany. Żadna niestandardowa logika, tokeny autoryzacji ani wstrzykiwanie nagłówków, które są zwykle obsługiwane w interceptorze, nie są stosowane do wstępnie pobranego zasobu HTML.

2. Faza nawigacji (aktywacja użytkownika)

Gdy aplikacja wyraźnie przechodzi do adresu URL (np. za pomocą WebViewCompat.navigate lub loadUrl) albo użytkownik kliknie pasujący link, WebView sprawdza, czy może użyć wstępnie pobranej pamięci podręcznej:

  • Ocena głównego kodu HTML: w tym momencie WebView wywoła shouldInterceptRequest() dla głównego kodu HTML. Aby strona została wyświetlona z pamięci podręcznej wstępnego pobierania, przechwytujący musi zwrócić wartość null. Jeśli zwrócisz niestandardowy obiekt WebResourceResponse, WebView uwzględni Twój przechwytujący i całkowicie pominie pamięć podręczną wstępnego pobierania.

  • Ocena zasobów podrzędnych: gdy wstępnie pobrany kod HTML zostanie dopuszczony do użycia,shouldInterceptRequest() zwykle uruchamia się w przypadku wszystkich kolejnych zasobów podrzędnych (takich jak obrazy, skrypty i arkusze CSS) wymaganych do zakończenia renderowania strony.

Najważniejsze zachowania

Sposób inicjowania i zarządzania żądaniami wstępnego pobierania przez WebView zależy od tych cech operacyjnych i sprawdzania kryteriów:

  • Bezpieczeństwo wątków: żądania można inicjować z dowolnego wątku.
  • Kwalifikacja: przed rozpoczęciem pobierania WebView sprawdza, czy żądanie jest bezpieczne i odpowiednie w danym kontekście. W tym celu sprawdza:
    • Istniejące pliki cookie: aby chronić prywatność użytkowników i zapobiegać efektom ubocznym podobnym do CSRF, WebView może pominąć wstępne pobieranie, jeśli żądanie wymaga określonych uwierzytelnionych plików cookie, które mogą spowodować zmianę stanu na serwerze.
    • Obecność skryptu service worker: jeśli skrypt service worker kontroluje już zakres adresu URL, WebView może przekazać żądanie do jego modułu obsługi pobierania zamiast inicjować standardowe pobieranie z wyprzedzeniem z sieci.
    • Dostępność serwera proxy: komponent WebView sprawdza, czy bieżąca ścieżka sieciowa (w tym skonfigurowane serwery proxy) jest stabilna, aby uniknąć niepowodzenia spekulacyjnych żądań w przypadku złożonych konfiguracji sieci.
  • Jeśli wstępne pobieranie nie rozpocznie się (nawet z prawidłowymi parametrami), często dzieje się tak, ponieważ komponent WebView uznał, że żądanie w tle może zakłócić bieżącą sesję użytkownika lub stan zabezpieczeń.
  • Anulowanie: użyj CancellationSignal, aby zakończyć żądanie w trakcie realizacji i zapobiec jego zapisaniu w pamięci podręcznej.

Renderowanie wstępne stron

Renderowanie wstępne tworzy ukryte „treści z internetu”, aby w pełni renderować stronę w tle, w tym wykonywać skrypty i pobierać zasoby podrzędne. Wstępne renderowanie korzysta z tej samej infrastruktury co wstępne pobieranie. Jeśli aplikacja zainicjuje wstępne renderowanie, komponent WebView najpierw pobierze wstępnie odpowiedź, aby obsłużyć nawigację wstępnego renderowania, unikając zbędnej aktywności sieciowej.

Implementacja

Wstępne renderowanie to operacja na poziomie instancji WebView. Wywołaj funkcję prerenderUrlAsync() za pomocą funkcji WebViewCompat z wątku UI.

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).
    }
});

Zarówno pobieranie z wyprzedzeniem, jak i wstępne renderowanie są w pełni asynchroniczne. prefetchUrlAsync() można wywołać z dowolnego wątku, a prerenderUrlAsync() musi być zainicjowany z wątku UI.

Ograniczenia techniczne

Aby zachować równowagę między natychmiastową nawigacją a stanem systemu, WebView wymusza te ograniczenia w czasie działania:

  • Niski poziom pamięci: WebView anuluje wstępnie wyrenderowane adresy URL, jeśli urządzenie ma mało pamięci RAM.
  • Niedozwolone interfejsy API: wszelkie próby uzyskania przez JavaScript dostępu do niektórych interfejsów API (np. odtwarzania dźwięku czy alertów) w kontekście tła spowodują natychmiastowe zakończenie wstępnego renderowania.
  • Limit instancji: w przypadku każdego elementu WebView obowiązuje limit aktywnych wstępnie renderowanych adresów URL.

Dopasowywanie adresów URL i No-Vary-Search (NVS)

Komponent WebView wymaga niezawodnego algorytmu dopasowywania, aby zapewnić, że wstępnie załadowany zasób będzie wyświetlany tylko w przypadku zamierzonej nawigacji.

Dopasowanie ścisłe a dopasowanie NVS

Domyślnie wstępne pobieranie i wstępne renderowanie wymagają dokładnego dopasowania adresu URL. Jeśli adres URL, do którego nastąpiło przekierowanie, jest identyczny z wstępnie załadowanym adresem URL, jest on natychmiast obsługiwany z pamięci podręcznej. Jeśli parametry zapytania są różne, komponent WebView stosuje te reguły No-Vary-Search (NVS):

  • Wskazówka: deweloperzy podają setExpectedNoVarySearchHeader() wskazówkę podczas inicjowania. Jeśli adres URL, do którego nastąpiło przejście, pasuje do adresu URL żądania bez parametrów sugerowanych, WebView na krótko blokuje działanie, aby poczekać na rzeczywiste nagłówki serwera.
  • Nagłówek serwera: nagłówek odpowiedzi NVS z serwera jest ostatecznym źródłem informacji. Jeśli serwer potwierdzi, że różnice w zapytaniu należy zignorować, dopasowanie zostanie wyświetlone z pamięci podręcznej. Jeśli nie, WebView wraca do zimnego wczytywania sieci.

Dyrektywa No-Vary-Search (NVS) jest przeznaczona do zaawansowanego użytku i większość deweloperów może jej nie potrzebować, ponieważ przekazują ten sam adres URL zarówno do pobierania z wyprzedzeniem, jak i do nawigacji (WebViewCompat.navigate lub loadUrl). Te wskazówki są potrzebne tylko wtedy, gdy występują różnice w parametrach zapytania między adresem URL pobierania z wyprzedzeniem a adresem URL nawigacji.

Konfiguracja globalna

Dostosuj działanie ładowania spekulacyjnego na poziomie profilu, konfigurując PrefetchCachelimity i maksymalną liczbę wstępnych renderowań. Możesz też przywrócić niestandardowe limity wstępnego pobierania do ustawień domyślnych:

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);

Obsługa błędów i wyjątków

Operacje spekulacyjne używają symbolu OutcomeReceiverCompat lub PrerenderOperationCallback do raportowania wyników.

Wyjątki podstawowe

Gdy operacja ładowania spekulacyjnego zakończy się niepowodzeniem, moduł obsługi błędów zgłosi jeden z tych podstawowych typów wyjątków, aby ułatwić Ci diagnozowanie konkretnych scenariuszy niepowodzenia:

  • PrefetchException: klasa bazowa dla wszystkich błędów asynchronicznego pobierania z wyprzedzeniem.
  • PrefetchNetworkException: oznacza błąd na poziomie sieci lub serwera. Może zawierać pole httpStatusCode (np. 404 lub 503), które pomaga w diagnozowaniu problemów po stronie serwera.
  • PrerenderException: klasa nadrzędna wszystkich błędów związanych z wstępnym renderowaniem, np. błędów spowodowanych brakiem pamięci lub użyciem w tle niedozwolonych interfejsów API (np. odtwarzania dźwięku).

Strategie optymalizacji

Aby zmaksymalizować korzyści wynikające z ładowania spekulacyjnego, a jednocześnie oszczędzać zasoby systemu, postępuj zgodnie z tymi zaleceniami:

  • Rozpocznij wcześnie: rozpocznij wstępne pobieranie podczas uruchamiania aplikacji lub gdy tylko pojawi się prawdopodobieństwo, że użytkownik przejdzie do miejsca docelowego nawigacji.
  • Łączenie wskazówek QUIC z wstępnym rozgrzewaniem połączenia: wywołaj addQuicHints() przed rozpoczęciem nawigacji preconnect(), prefetchUrlAsync() lub standardowej, aby mieć pewność, że komponent WebView podejmie próbę nawiązania połączeń za pomocą protokołu HTTP/3.
  • Strategia zintegrowana: jeśli wstępnie renderujesz adres URL, który jest już w pamięci podręcznej wstępnego pobierania, nawigacja wstępnego renderowania jest obsługiwana z tej pamięci podręcznej, co pozwala uniknąć zbędnych żądań sieciowych.
  • Monitoruj limity: wstępne renderowanie jest zasobożerne. W przypadku kilku prawdopodobnych kandydatów preferuj wstępne pobieranie, a wstępne renderowanie rezerwuj dla najbardziej prawdopodobnej nawigacji.
  • Obsługa schematu: upewnij się, że wszystkie adresy URL używają obowiązkowego schematu HTTPS. Nieprawidłowe schematy lub puste dane wejściowe wywołują synchroniczny błąd IllegalArgumentException.

Dodatkowe materiały

Więcej informacji o debugowaniu aplikacji internetowych, optymalizowaniu wydajności uruchamiania komponentu WebView i obsłudze zakończenia procesu renderowania znajdziesz w tych materiałach: