Pemuatan spekulatif di WebView

Latensi navigasi adalah metrik penting untuk pengalaman pengguna. Untuk membantu developer mengurangi latensi ini, WebView menyediakan API untuk pemuatan spekulatif, yang memungkinkan aplikasi Anda mengambil atau merender konten sebelum pengguna secara eksplisit membuka konten tersebut.

WebView mendukung tiga jenis utama pemuatan spekulatif: Preconnect, Prefetch, dan Prerender.

Dengan menerapkan strategi pemuatan spekulatif, Anda dapat mencapai hal berikut:

  • Pengurangan signifikan dalam latensi pemuatan konten web: Pindahkan waktu mulai jaringan ke lebih awal dalam siklus proses aplikasi.
  • Tingkat keberhasilan navigasi yang lebih tinggi: Dengan melakukan pemanasan awal jaringan dan cache, navigasi cenderung tidak akan gagal karena masalah jaringan sementara.
  • Responsivitas yang lebih baik: Khususnya pra-rendering, memungkinkan transisi instan yang membuat aplikasi terasa jauh lebih cepat.

Memilih strategi pemuatan spekulatif

Pembeda utama antara strategi ini terletak pada cakupannya: Preconnect API berbasis origin, yang berarti hanya memerlukan domain target. Prefetch dan Prerender API berbasis URL, yang berarti memerlukan jalur halaman web yang tepat.

Karena Preconnect beroperasi di tingkat origin, Preconnect dapat dimulai jauh lebih awal dalam siklus proses aplikasi, bahkan sebelum Anda mengetahui konten atau halaman tertentu yang akan dibuka pengguna.

Tabel berikut membandingkan ketiga strategi ini untuk membantu Anda memilih strategi yang tepat untuk kasus penggunaan Anda:

Fitur Preconnect Prefetch Prerender
Sasaran utama Memanaskan koneksi Hanya menyimpan HTML ke cache (tanpa JavaScript atau CSS) Melakukan pra-render seluruh halaman
Cakupan Tingkat profil (dibagikan di seluruh WebView) Tingkat profil (dibagikan di seluruh WebView) Tingkat WebView (terikat ke WebView tertentu)
Jetpack WebKit API androidx.webkit.Profile androidx.webkit.Profile androidx.webkit.WebViewCompat
Metode API inti preconnect(...) prefetchUrlAsync(...) prerenderUrlAsync(...)
Konfigurasi T/A PrefetchCache.setMaxPrefetches()
PrefetchCache.setPrefetchTtlSeconds()
setMaxPrerenders()
Penggunaan resource Rendah (jaringan) Sedang (jaringan, memori) Tinggi (CPU, memori, jaringan)
Kapan digunakan Saat origin target diketahui, tetapi URL tertentu belum ditentukan. Saat URL yang tepat diketahui dan navigasi kemungkinan akan terjadi, dengan cache yang dibagikan di seluruh WebView. Saat URL yang tepat diketahui dan navigasi sangat pasti dalam WebView tertentu.
Manfaat Penyiapan koneksi yang lebih cepat untuk URL apa pun di origin Pemuatan jaringan yang lebih cepat untuk URL yang cocok Navigasi yang benar-benar instan saat diaktifkan

Melakukan prakoneksi ke origin

Prakoneksi mempercepat pemuatan di masa mendatang dengan melakukan pencarian DNS dan handshake TCP/TLS secara preemptif untuk origin tertentu.

Tidak seperti Prefetch dan Prerender, yang memerlukan URL tujuan yang tepat, Preconnect berbasis origin. Hal ini memungkinkan Anda melakukan panggilan Preconnect jauh lebih awal daripada Prefetch dan Prerender.

Strategi tingkat Profil dengan resource rendah ini mengurangi latensi awal untuk setiap WebView yang berbagi Profil tersebut, asalkan origin belum pernah dikunjungi. Koneksi tetap terbuka selama sekitar 30 detik, sehingga menguntungkan permintaan HTTP, navigasi, atau subresource lintas origin berikutnya dengan menghilangkan overhead handshake.

Penerapan

Untuk memulai prakoneksi, panggil preconnect(String url) pada instance Profile. API ini harus dipanggil di UI thread dan memerlukan WebViewFeature.PRECONNECT untuk didukung.

API beroperasi di origin, tetapi untuk kemudahan, URL lengkap dapat diberikan (seperti https://www.example.com/index.html). Hal ini otomatis diperlakukan sebagai panggilan ke origin (misalnya, https://www.example.com). Beberapa origin dapat terhubung dengan memanggil API ini beberapa kali.

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
}

Konfigurasi umum: PrefetchParameters dan PrerenderParameters

Prefetch dan Prerender menggunakan PrefetchParameters atau PrerenderParameters untuk menyesuaikan permintaan. Class ini memungkinkan Anda memberikan header dan petunjuk tambahan untuk pencocokan URL, seperti konfigurasi 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();

Melakukan prefetch konten

Prefetch mendownload resource HTML utama URL dan menyimpannya di cache jaringan profil. Di WebView, Profile bertindak sebagai penampung data browser, termasuk cookie, cache HTTP, dan pekerja layanan. Karena prefetch adalah operasi tingkat profil, setiap WebView yang terkait dengan profil tersebut dapat memanfaatkan respons yang di-cache.

Penerapan

Untuk memulai prefetch, panggil prefetchUrlAsync() pada instance Profile. Operasi ini hanya mendukung skema 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
            }
        }
    }
);

Siklus proses intersepsi

Permintaan prefetch WebView mengubah waktu dan cara callback shouldInterceptRequest() dipicu. Karena hal ini berdampak langsung pada apakah konten yang di-prefetch berhasil digunakan, penting untuk memahami siklus proses dua langkah:

Diagram yang menunjukkan siklus proses pencegahan pengambilan data di awal WebView dua langkah
  selama fase spekulatif dan navigasi.
Gambar 1. Siklus proses intersepsi dua langkah untuk permintaan dan navigasi prefetch WebView.

1. Fase spekulatif (permintaan Prefetch)

Saat prefetchUrlAsync() dipanggil, WebView akan mendownload resource HTML utama di latar belakang. shouldInterceptRequest() sepenuhnya dilewati untuk permintaan latar belakang ini. Setiap logika kustom, token otorisasi, atau injeksi header yang biasanya ditangani di dalam interseptor Anda tidak diterapkan ke resource HTML yang di-prefetch.

2. Fase navigasi (aktivasi pengguna)

Saat aplikasi secara eksplisit membuka URL (misalnya, menggunakan loadUrl()) atau pengguna mengklik link yang cocok, WebView akan menentukan apakah dapat menggunakan cache yang di-prefetch:

  • Evaluasi HTML utama: WebView akan memicu shouldInterceptRequest() untuk HTML utama saat ini. Agar halaman berhasil ditayangkan dari cache prefetch, interseptor Anda harus menampilkan null. Jika Anda menampilkan WebResourceResponse kustom, WebView akan menghormati interseptor Anda dan sepenuhnya melewati cache prefetch.

  • Evaluasi sub-resource: Setelah HTML yang di-prefetch dihapus untuk digunakan, shouldInterceptRequest() akan dipicu secara normal untuk semua sub-resource berikutnya (seperti gambar, skrip, dan CSS) yang diperlukan untuk menyelesaikan rendering halaman.

Perilaku utama

Karakteristik operasional dan pemeriksaan kelayakan berikut mengatur cara WebView memulai dan mengelola permintaan prefetch:

  • Keamanan thread: Permintaan dapat dimulai dari thread mana pun.
  • Kelayakan: Sebelum memulai pengambilan data, WebView memastikan permintaan aman dan sesuai konteks dengan memeriksa hal berikut:
    • Cookie yang ada: Untuk melindungi privasi pengguna dan mencegah efek samping seperti CSRF, WebView mungkin akan melewati prefetching jika permintaan memerlukan cookie autentikasi tertentu yang dapat memicu perubahan status di server.
    • Keberadaan service worker: Jika service worker sudah mengontrol cakupan URL, WebView dapat menangguhkan ke handler pengambilan data service worker, bukan memulai prefetch jaringan standar.
    • Ketersediaan proxy: WebView memverifikasi bahwa jalur jaringan saat ini (termasuk proxy yang dikonfigurasi) stabil untuk menghindari kegagalan permintaan spekulatif dalam konfigurasi jaringan yang kompleks.
  • Jika prefetch gagal dimulai (bahkan dengan parameter yang valid), hal ini sering kali disebabkan karena WebView telah menentukan bahwa permintaan latar belakang dapat mengganggu sesi atau status keamanan pengguna saat ini.
  • Pembatalan: Gunakan CancellationSignal untuk menghentikan permintaan yang sedang berlangsung dan mencegahnya di-cache.

Melakukan pra-render halaman

Pra-rendering membuat "konten web" tersembunyi untuk merender halaman sepenuhnya di latar belakang, termasuk eksekusi skrip dan pengambilan subresource. Pra-rendering mengandalkan infrastruktur dasar yang sama dengan prefetching. Jika aplikasi memulai pra-render, WebView akan melakukan prefetch respons terlebih dahulu untuk menayangkan navigasi pra-render, sehingga menghindari aktivitas jaringan yang berlebihan.

Penerapan

Pra-rendering adalah operasi tingkat instance WebView. Panggil prerenderUrlAsync() menggunakan WebViewCompat dari UI thread.

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

Prefetch dan pra-render sepenuhnya bersifat asinkron. prefetchUrlAsync() dapat dipanggil dari thread mana pun, sedangkan prerenderUrlAsync() harus dimulai dari UI thread.

Batasan teknis

Untuk menyeimbangkan navigasi instan dengan kesehatan sistem, WebView menerapkan batasan runtime berikut:

  • Tekanan memori: WebView membatalkan URL yang telah di-pra-render jika perangkat memiliki RAM yang rendah.
  • API yang tidak diizinkan: Setiap upaya oleh JavaScript untuk mengakses API tertentu (misalnya, pemutaran audio, pemberitahuan) dalam konteks latar belakang akan segera menghentikan pra-render.
  • Batas instance: Ada batasan jumlah URL yang di-pra-render aktif yang diizinkan per WebView.

Pencocokan URL dan No-Vary-Search (NVS)

WebView memerlukan algoritma pencocokan yang andal untuk memastikan resource yang telah dimuat sebelumnya hanya ditayangkan untuk navigasi yang dimaksudkan.

Pencocokan persis versus pencocokan NVS

Secara default, prefetch dan pra-render memerlukan pencocokan URL yang persis. Jika URL yang dibuka identik dengan URL yang telah dimuat sebelumnya, URL tersebut akan segera ditayangkan dari cache. Jika parameter kueri berbeda, WebView akan menggunakan aturan No-Vary-Search (NVS) berikut:

  • Petunjuk: Developer memberikan petunjuk setExpectedNoVarySearchHeader() selama inisiasi. Jika URL yang dibuka cocok dengan URL permintaan dikurangi parameter yang diberi petunjuk, WebView akan memblokir sebentar untuk menunggu header aktual server.
  • Header server: Header respons NVS dari server adalah otoritas tertinggi. Jika server mengonfirmasi bahwa perbedaan kueri harus diabaikan, kecocokan akan ditayangkan dari cache. Jika tidak, WebView akan kembali ke pemuatan jaringan dingin.

No-Vary-Search (NVS) ditujukan untuk penggunaan lanjutan, dan sebagian besar developer mungkin tidak memerlukannya karena mereka meneruskan URL yang sama persis ke prefetch dan loadUrl(). Panduan ini hanya diperlukan jika ada perbedaan parameter kueri antara URL prefetch dan URL loadUrl().

Konfigurasi global

Sesuaikan perilaku pemuatan spekulatif di tingkat profil dengan mengonfigurasi batas PrefetchCache dan pra-render maksimum. Anda juga dapat mereset batas prefetch kustom kembali ke default sistem:

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

Penanganan error dan pengecualian

Operasi spekulatif menggunakan OutcomeReceiverCompat atau PrerenderOperationCallback untuk melaporkan hasil.

Pengecualian utama

Jika operasi pemuatan spekulatif gagal, pengendali error Anda akan melaporkan salah satu jenis pengecualian utama berikut untuk membantu Anda mendiagnosis skenario kegagalan tertentu:

  • PrefetchException: Class dasar untuk semua error prefetch asinkron.
  • PrefetchNetworkException: Menunjukkan kegagalan tingkat jaringan atau server. Error ini dapat menyertakan kolom httpStatusCode (seperti, 404 atau 503) untuk membantu mendiagnosis masalah sisi server.
  • PrerenderException: Superclass untuk semua error terkait pra-render, seperti kegagalan karena tekanan memori atau penggunaan API yang tidak diizinkan (seperti pemutaran audio) di latar belakang.

Strategi pengoptimalan

Ikuti rekomendasi ini untuk memaksimalkan manfaat pemuatan spekulatif sekaligus menghemat resource sistem:

  • Mulai lebih awal: Mulai prefetching selama startup aplikasi atau segera setelah tujuan navigasi kemungkinan akan terjadi.
  • Strategi terintegrasi: Jika Anda melakukan pra-render URL yang sudah ada di cache prefetch, navigasi pra-render akan ditayangkan dari cache tersebut, sehingga menghindari permintaan jaringan yang berlebihan.
  • Pantau kuota: Pra-rendering menggunakan banyak resource. Sebaiknya lakukan prefetching untuk beberapa kandidat yang mungkin dan simpan pra-rendering untuk satu navigasi yang paling mungkin.
  • Dukungan skema: Pastikan semua URL menggunakan skema HTTPS wajib. Skema yang tidak valid atau input null akan memicu IllegalArgumentException sinkron.

Referensi lainnya

Untuk mempelajari lebih lanjut cara men-debug aplikasi web, mengoptimalkan performa startup WebView, dan menangani penghentian proses perender, lihat referensi berikut: