Saat aplikasi Anda pertama kali menggunakan WebView, sistem akan melakukan tugas startup tertentu.
Proses startup ini berat. Secara default, hal ini terjadi secara implisit di thread UI saat aplikasi memanggil banyak API dalam paket android.webkit atau androidx.webkit untuk pertama kalinya, atau mem-inflate tata letak yang berisi tag WebView.
Mengapa hal ini penting
Karena startup implisit ini terjadi sepenuhnya di thread utama, aplikasi Anda akan terblokir dari pemrosesan input pengguna dan risiko error Aplikasi Tidak Merespons (ANR) akan meningkat secara drastis. Untuk mengetahui informasi selengkapnya tentang cara Android menangani model eksekusi thread tunggal, lihat Ringkasan proses dan thread.
Pemicu untuk startup implisit
Startup implisit dapat dipicu dengan cara berikut:
- Secara terprogram: Memanggil API seperti
WebSettings.getUserAgentString(). - Menggunakan tata letak: Memanggil
setContentView()ataulayoutInflater.inflate()pada resource XML yang menyertakan<WebView>.
Startup implisit juga dapat berdampak negatif pada metrik bisnis Anda, seperti waktu
startup aplikasi dan waktu hingga tampilan pertama. Jika inisialisasi implisit tidak
optimal untuk aplikasi Anda, gunakan startUpWebView.
Halaman ini membahas cara mengoptimalkan performa peluncuran WebView menggunakan
startUpWebView API.
Mengontrol peluncuran WebView
Untuk meningkatkan performa dan meminimalkan ANR, gunakan startUpWebView API yang tersedia
di library Jetpack Webkit. API ini memberi Anda kontrol eksplisit atas kapan
WebView dimulai. Hal ini memindahkan sebagian besar beban kerja startup ke thread latar belakang dan memungkinkan pekerjaan apa pun yang harus dilakukan di UI thread dilakukan dalam beberapa bagian, bukan satu blok monolitik besar. Hal ini membebaskan thread UI Anda untuk menangani tugas aplikasi penting lainnya secara paralel, sehingga mengurangi kemungkinan memblokir pengalaman pengguna.
API ini menggunakan callback androidx.webkit.WebViewOutcomeReceiver, sehingga Anda dapat melacak inisialisasi yang berhasil.
Untuk menggunakan API ini, tambahkan library Jetpack Webkit ke file build.gradle Anda.
Pastikan Anda menggunakan versi 1.16.0 atau yang lebih tinggi:
dependencies {
implementation("androidx.webkit:webkit:1.16.0")
}
Menggunakan startUpWebView API
Cara mengoptimalkan alur mulai bergantung pada kapan aplikasi Anda benar-benar perlu menampilkan WebView.
Saat WebView tidak berada di jalur kritis
Jika aplikasi Anda tidak perlu memuat WebView dengan segera, Anda dapat menyembunyikan biaya inisialisasi sepenuhnya. Panggil startUpWebView di awal siklus proses aplikasi Anda dan tunggu callback keberhasilan dipicu.
Idealnya, Anda harus menunggu callback sebelum memanggil API WebView lainnya. Jika
Anda memicu startUpWebView, tetapi tidak menunggu hingga selesai sebelum menyentuh
komponen WebView lainnya, sistem akan memblokir UI thread saat menunggu
inisialisasi selesai. Aplikasi Anda mungkin mendapatkan beberapa manfaat performa dari
tugas latar belakang yang telah selesai, tetapi bukan manfaat maksimum.
Saat WebView berada di jalur kritis
Jika perjalanan pengguna inti aplikasi Anda memerlukan WebView dengan segera, Anda mungkin tidak dapat menunggu hingga startup WebView selesai. Dalam skenario ini, Anda
tetap harus memanggil startUpWebView sedini mungkin dalam siklus proses aplikasi
(seperti dalam Application.onCreate), tetapi jangan menunggu callback
dipicu. Sebagai gantinya, gunakan WebView API secara langsung jika diperlukan.
Untuk mendapatkan manfaat maksimal dari startup asinkron, tunda secara signifikan pembuatan instance WebView atau pemanggilan API WebView hingga tidak ada lagi operasi UI thread jalur penting yang tersisa untuk dijalankan (seperti memperluas hierarki tata letak, menginisialisasi SDK lain, atau menggambar frame awal).
Jika Anda memanggil startUpWebView dan segera memanggil WebView API setelahnya di thread utama, UI thread akan diblokir sambil menunggu inisialisasi selesai. Dalam skenario ini, tidak ada manfaat performa.
Jika penggunaan WebView dapat menjadi jalur penting, tetapi Anda tidak ingin memulai WebView sepenuhnya, Anda dapat memilih untuk menjalankan tugas startup WebView secara selektif yang dapat berjalan di thread latar belakang, sehingga membebaskan thread UI untuk tugas penting aplikasi lainnya. Untuk melakukannya, Anda dapat menggunakan
shouldRunUiThreadStartUpTasks(false).
Selanjutnya dalam siklus proses aplikasi, Anda dapat memanggil startUpWebView lagi dengan
shouldRunUiThreadStartUpTasks(true) untuk menyelesaikan tugas startup yang tersisa di
thread UI. Apakah Anda menunggu callback pada saat itu bergantung pada
apakah penggunaan WebView berada di jalur penting.
Contoh penerapan
API ini menggunakan callback androidx.webkit.WebViewOutcomeReceiver, yang memungkinkan Anda
melacak inisialisasi yang berhasil atau menangani kegagalan diagnostik.
startUpWebView dapat dipanggil beberapa kali dengan aman dari berbagai bagian aplikasi Anda. Sebaiknya hindari penerapan loop percobaan ulang yang tidak efektif.
Contoh kode berikut menunjukkan cara menggunakan
WebViewCompat.startUpWebView API untuk inisialisasi asinkron.
Kotlin
import android.content.Context
import android.util.Log
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewOutcomeReceiver
import androidx.webkit.WebViewStartUpConfig
import androidx.webkit.WebViewStartUpResult
import androidx.webkit.WebViewStartupException
import java.util.concurrent.Executors
fun initializeWebView(context: Context) {
// 1. Create a startup configuration specifying the background thread
// that WebView will use to run its initialization tasks.
val startUpConfig = WebViewStartUpConfig.Builder(
Executors.newSingleThreadExecutor()
).build()
// 2. Trigger WebView startup asynchronously
WebViewCompat.startUpWebView(
context,
startUpConfig,
object : WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException> {
override fun onResult(result: WebViewStartUpResult) {
// Success: The WebView has finished its background initialization.
// This callback is guaranteed to be invoked on the UI thread.
setupWebView()
}
override fun onError(error: WebViewStartupException) {
// Failure: The initialization encountered a startup exception.
Log.e("WebViewStartup", "Failed to initialize WebView", error)
}
}
)
}
Java
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewOutcomeReceiver;
import androidx.webkit.WebViewStartUpConfig;
import androidx.webkit.WebViewStartUpResult;
import androidx.webkit.WebViewStartupException;
import java.util.concurrent.Executors;
public void initializeWebView(Context context) {
// 1. Create the startup configuration specifying the background thread pool
// to handle internal non-UI initialization processes.
WebViewStartUpConfig startUpConfig = new WebViewStartUpConfig.Builder(
Executors.newSingleThreadExecutor()
).build();
// 2. Trigger WebView startup asynchronously
WebViewCompat.startUpWebView(
context,
startUpConfig,
new WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException>() {
@Override
public void onResult(@NonNull WebViewStartUpResult result) {
// Success: The WebView has finished its background initialization.
// This callback is invoked directly on the UI thread.
setupWebView();
}
@Override
public void onError(@NonNull WebViewStartupException error) {
// Failure: Handled using the concrete WebViewStartupException
Log.e("WebViewStartup", "Failed to initialize WebView", error);
}
}
);
}
Men-debug masalah startup asinkron
Jika startUpWebView tidak memberikan manfaat performa yang diharapkan, hal ini sering kali disebabkan karena WebView diinisialisasi secara implisit di tempat lain dalam aplikasi Anda sebelum panggilan Anda dieksekusi. Hal ini mungkin disebabkan oleh alasan berikut:
Library pihak ketiga atau SDK diinisialisasi lebih awal dalam siklus proses aplikasi.
ContentProvidersyang disuntikkan ke dalam APK Anda yang memicu WebView API selama startup aplikasi.Inflasi tata letak atau panggilan terprogram (seperti mengambil string agen pengguna) yang terjadi terlalu dini secara tidak terduga.
Untuk membantu Anda mendiagnosis di mana dan mengapa inisialisasi yang tidak terduga ini terjadi, objek WebViewStartUpResult menyediakan kemampuan audit bawaan:
getUiThreadBlockingStartUpLocations(): Menampilkan daftar objekStartUpLocationyang merepresentasikan lokasi tempat tugas startup WebView memblokir thread UI utama.getNonUiThreadBlockingStartUpLocations(): Menampilkan situs panggilan tertentu tempat tugas startup yang sedang berjalan memblokir thread latar belakang.
Setiap StartUpLocation berisi stack trace yang dapat Anda catat atau periksa untuk
menemukan class dan metode yang tepat yang memicu inisialisasi.
Contoh penerapan
Anda dapat memeriksa lokasi ini di dalam callback onResult untuk mengaudit jalur
mulai:
override fun onResult(result: WebViewStartUpResult) {
// Check if WebView startup was blocked on the UI thread prior to or during initialization
val uiBlockingLocations = result.getUiThreadBlockingStartUpLocations()
if (!uiBlockingLocations.isNullOrEmpty()) {
for (location in uiBlockingLocations) {
// Log the stack trace of the call site that triggered the UI-blocking startup
Log.w("WebViewDebug", "WebView startup blocked the UI thread here:", location.getStack())
}
} else {
Log.i("WebViewDebug", "Excellent! No UI-blocking WebView startup detected.")
}
// Check where background initialization tasks were executed
val backgroundLocations = result.getNonUiThreadBlockingStartUpLocations()
backgroundLocations?.forEach { location ->
Log.d("WebViewDebug", "WebView background startup occurred at: ${location.getStack()}")
}
setupWebView()
}
Cara menggunakan data ini selama audit
Saat mengaudit peluncuran WebView aplikasi, gunakan strategi berikut untuk menganalisis data diagnostik dan mengatasi hambatan performa:
Cari stack trace yang tidak terduga: Jika
getUiThreadBlockingStartUpLocations()tidak kosong, lihat stack trace yang dicetak. Jika Anda melihat class yang termasuk dalam SDK pihak ketiga atau komponen yang tidak terduga, Anda telah menemukan hambatan inisialisasi implisit.Verifikasi urutan panggilan: Jika output log Anda menunjukkan bahwa inisialisasi implisit terjadi sebelum panggilan
startUpWebViewmanual, Anda harus memindahkan inisialisasistartUpWebViewlebih awal di aplikasi Anda atau mengonfigurasi SDK yang bermasalah untuk menunda tugas yang bergantung pada WebView.
Bermigrasi dari solusi sebelumnya
Sebelumnya, Anda mungkin menggunakan solusi sementara eksplisit untuk memaksa inisialisasi WebView di thread latar belakang, seperti mengambil string agen pengguna.
Solusi ini dianggap sebagai praktik yang tidak didukung, dan perilaku dasarnya dapat berubah dalam rilis mendatang. Jika aplikasi Anda mengandalkan solusi sementara yang eksplisit dan tidak terdokumentasi untuk memicu atau mengelola startup WebView, sebaiknya gunakan API startUpWebView. API startUpWebView berfungsi di semua
versi Android dan WebView yang didukung oleh library Jetpack Webkit.
Penggunaan implementasi Jetpack Webkit membantu memastikan perilaku yang konsisten di seluruh ekosistem Android. Keunggulan utama API ini adalah ketahanannya: di perangkat lama yang tidak memiliki pengoptimalan yang lebih baru, API ini mempertahankan paritas performa dengan solusi manual. Hal ini memungkinkan Anda menggunakan manfaat startup modern di perangkat yang lebih baru tanpa menimbulkan penurunan performa di perangkat yang lebih lama.
Jika Anda mengalami masalah atau memiliki masukan tentang startUpWebView API, laporkan
bug di issue tracker publik.