Beralih ke WebView di Android 4.4

Android 4.4 (API level 19) memperkenalkan versi baru WebView yang didasarkan pada Chromium. Perubahan ini mengupgrade dukungan performa dan standar WebView untuk HTML5, CSS3, dan JavaScript guna mencocokkan dengan versi browser web terbaru. Setiap aplikasi yang menggunakan WebView akan mewarisi upgrade versi tersebut jika dijalankan di Android 4.4 dan yang lebih tinggi.

Dokumen ini menjelaskan perubahan tambahan yang perlu Anda perhatikan untuk WebView jika Anda menetapkan targetSdkVersion ke "19" atau yang lebih tinggi.

Catatan: Jika targetSdkVersion Anda ditetapkan ke "18" atau yang lebih rendah, WebView akan beroperasi dalam "quirks mode" untuk menghindari beberapa perubahan perilaku yang dijelaskan di bawah, sebaik mungkin—sambil tetap memberikan aplikasi Anda upgrade standar performa dan web. Namun, perhatikan bahwa tata letak kolom tunggal dan sempit dan tingkat zoom default tidak didukung sama sekali di Android 4.4, dan mungkin ada perbedaan perilaku yang belum teridentifikasi, jadi pastikan untuk menguji aplikasi Anda di Android 4.4 atau yang lebih tinggi, meskipun Anda tetap menetapkan targetSdkVersion ke "18" atau yang lebih rendah.

Untuk membantu Anda mengatasi masalah yang mungkin Anda temui saat memigrasikan aplikasi ke WebView di Android 4.4, Anda dapat mengaktifkan debug jarak jauh melalui Chrome di desktop dengan memanggil setWebContentsDebuggingEnabled(). Fitur baru di WebView ini memungkinkan Anda memeriksa dan menganalisis konten web, skrip, dan aktivitas jaringan saat dijalankan dalam WebView. Untuk informasi selengkapnya, lihat Proses Debug Jarak Jauh di Android.

Perubahan Agen-Pengguna

Jika Anda menayangkan konten ke WebView berdasarkan agen-pengguna, harap perhatikan bahwa string agen-pengguna telah berubah sedikit dan sekarang menyertakan versi Chrome:

    Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16H) AppleWebKit/537.36
    (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
    

Jika Anda perlu mengambil agen-pengguna, tetapi tidak ingin menyimpannya untuk aplikasi atau tidak ingin membuat instance WebView, Anda harus menggunakan metode statis, getDefaultUserAgent(). Namun, jika Anda ingin mengganti string agen-pengguna di WebView, Anda dapat menggunakan getUserAgentString().

Multi-threading dan Pemblokiran Thread

Jika Anda memanggil metode di WebView dari thread apa pun selain UI thread aplikasi Anda, hal ini dapat menyebabkan hasil yang tidak diharapkan. Misalnya, jika aplikasi Anda menggunakan beberapa thread, Anda dapat menggunakan metode runOnUiThread() untuk memastikan kode berjalan di UI thread:

Kotlin

    runOnUiThread {
        // Code for WebView goes here
    }
    

Java

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            // Code for WebView goes here
        }
    });
    

Selain itu, pastikan Anda tidak pernah memblokir UI thread. Situasi saat beberapa aplikasi membuat kesalahan ini adalah saat menunggu callback JavaScript. Misalnya, jangan menggunakan kode seperti ini:

Kotlin

    // This code is BAD and will block the UI thread
    webView.loadUrl("javascript:fn()")
    while (result == null) {
        Thread.sleep(100)
    }
    

Java

    // This code is BAD and will block the UI thread
    webView.loadUrl("javascript:fn()");
    while(result == null) {
      Thread.sleep(100);
    }
    

Sebagai gantinya, Anda dapat menggunakan metode baru, evaluateJavascript(), untuk menjalankan JavaScript secara asinkron.

Penanganan URL Kustom

WebView yang baru menerapkan pembatasan tambahan saat meminta resource dan menyelesaikan link yang menggunakan skema URL kustom. Misalnya, jika Anda menerapkan callback seperti shouldOverrideUrlLoading() atau shouldInterceptRequest(), maka WebView akan memanggil ketiganya hanya untuk URL yang valid.

Jika Anda menggunakan skema URL kustom atau URL dasar dan melihat bahwa aplikasi menerima panggilan yang lebih sedikit ke callback ini atau gagal memuat resource di Android 4.4, pastikan bahwa permintaan menentukan URL yang valid yang sesuai dengan RFC 3986.

Misalnya, WebView yang baru mungkin tidak memanggil metode shouldOverrideUrlLoading() Anda untuk link seperti ini:

<a href="showProfile">Show Profile</a>

Hasil dari pengguna yang mengklik link tersebut dapat bervariasi:

  • Jika Anda memuat halaman dengan memanggil loadData() atau loadDataWithBaseURL() dengan URL dasar yang tidak valid atau null, maka Anda tidak akan menerima callback shouldOverrideUrlLoading() untuk jenis link ini di halaman.

    Catatan: Jika Anda menggunakan loadDataWithBaseURL() dan URL dasarnya tidak valid atau ditetapkan ke null, semua link dalam konten yang Anda muat harus absolut.

  • Jika Anda memuat halaman dengan memanggil loadUrl() atau memberikan URL dasar yang valid dengan loadDataWithBaseURL(), maka Anda akan menerima callback shouldOverrideUrlLoading() untuk jenis link ini pada halaman, tetapi URL yang Anda terima akan berupa URL absolut, relatif terhadap halaman saat ini. Misalnya, URL yang Anda terima akan berupa "http://www.example.com/showProfile", bukan hanya "showProfile".

Daripada menggunakan string simpel di link seperti yang ditunjukkan di atas, Anda dapat menggunakan skema kustom seperti berikut:

<a href="example-app:showProfile">Show Profile</a>

Anda kemudian dapat menangani URL ini di metode shouldOverrideUrlLoading() Anda seperti ini:

Kotlin

    // The URL scheme should be non-hierarchical (no trailing slashes)
    const val APP_SCHEME = "example-app:"

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        return if (url?.startsWith(APP_SCHEME) == true) {
            urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
            respondToData(urlData)
            true
        } else {
            false
        }
    }
    

Java

    // The URL scheme should be non-hierarchical (no trailing slashes)
    private static final String APP_SCHEME = "example-app:";

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith(APP_SCHEME)) {
            urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
            respondToData(urlData);
            return true;
        }
        return false;
    }
    

Jika Anda tidak dapat mengubah HTML, Anda mungkin dapat menggunakan loadDataWithBaseURL() dan menetapkan URL dasar yang terdiri dari skema kustom dan host yang valid, seperti "example-app://<valid_host_name>/". Contoh:

Kotlin

    webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA, null, "UTF-8", null)
    

Java

    webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA,
            null, "UTF-8", null);
    

Nama host yang valid harus sesuai dengan RFC 3986 dan penting untuk menyertakan garis miring di bagian akhir, jika tidak, permintaan apa pun dari halaman yang dimuat dapat dibuang.

Perubahan Area Pandang

Area pandang target-densitydpi tidak didukung lagi.

Sebelumnya, WebView mendukung properti area pandang bernama target-densitydpi untuk membantu halaman web menentukan kepadatan layar yang diinginkan. Properti ini tidak lagi didukung dan Anda harus melakukan migrasi menggunakan solusi standar dengan gambar dan CSS seperti yang dibahas dalam UI Pixel-Perfect di WebView.

Area pandang diperbesar saat kecil

Sebelumnya, jika Anda menetapkan lebar area pandang dengan nilai kurang dari atau sama dengan "320", maka area pandang akan ditetapkan ke "lebar perangkat", dan jika Anda menetapkan tinggi area pandang dengan nilai kurang dari atau sama dengan tinggi WebView, maka area pandang akan ditetapkan ke "tinggi-perangkat". Namun, saat berjalan di WebView baru, nilai lebar atau tinggi akan dipatuhi dan WebView diperbesar untuk mengisi lebar layar.

Beberapa tag area pandang tidak didukung

Sebelumnya, jika Anda menyertakan beberapa tag area pandang di halaman web, WebView akan menggabungkan properti dari semua tag. Pada WebView baru, hanya area pandang terakhir yang digunakan dan area pandang lainnya akan diabaikan.

Zoom default tidak digunakan lagi

Metode getDefaultZoom() dan setDefaultZoom() untuk mendapatkan dan menetapkan tingkat zoom awal pada halaman tidak lagi didukung, dan sebaiknya Anda menentukan area pandang yang sesuai di halaman web.

Perhatian: API ini tidak didukung sama sekali di Android 4.4 dan yang lebih tinggi. Meskipun targetSdkVersion Anda disetel ke "18" atau yang lebih rendah, API ini tidak berpengaruh.

Untuk informasi tentang cara menentukan properti area pandang di HTML, baca Pixel-Perfect UI di WebView.

Jika Anda tidak dapat menetapkan lebar area pandang dalam HTML, panggil setUseWideViewPort() untuk memastikan halaman diberi area pandang yang lebih luas. Contoh:

Kotlin

    webView.settings.apply {
        useWideViewPort = true
        loadWithOverviewMode = true
    }
    

Java

    WebSettings settings = webView.getSettings();
    settings.setUseWideViewPort(true);
    settings.setLoadWithOverviewMode(true);
    

Perubahan Gaya

Latar belakang CSS singkat mengganti ukuran-latar

Chrome dan browser lainnya telah berperilaku seperti ini untuk sementara waktu, tetapi sekarang WebView juga akan mengganti setelan CSS untuk background-size jika Anda juga menentukan gaya background. Misalnya, ukuran di bawah ini akan disetel ulang ke nilai default:

    .some-class {
      background-size: contain;
      background: url('images/image.png') no-repeat;
    }
    

Cara memperbaikinya adalah dengan mengganti kedua properti tersebut.

    .some-class {
      background: url('images/image.png') no-repeat;
      background-size: contain;
    }
    

Ukuran berbentuk piksel CSS, bukan piksel layar

Sebelumnya, parameter ukuran seperti window.outerWidth dan window.outerHeight menampilkan nilai dalam piksel layar yang sebenarnya. Pada WebView baru, ini akan menampilkan nilai berdasarkan piksel CSS.

Secara umum, mencoba dan menghitung ukuran fisik dalam piksel untuk menentukan ukuran elemen atau penghitungan lainnya merupakan praktik yang tidak baik. Namun, jika Anda telah menonaktifkan zoom dan skala awal ditetapkan ke 1,0, Anda dapat menggunakan window.devicePixelRatio untuk mendapatkan skala, lalu kalikan nilai piksel CSS dengan nilai tersebut. Sebaliknya, Anda juga dapat membuat JavaScript binding untuk mengkueri ukuran piksel dari WebView itu sendiri.

Untuk informasi selengkapnya, lihat quirksmode.org.

NARROW_COLUMNS dan SINGLE_COLUMN tidak didukung lagi

Nilai NARROW_COLUMNS untuk WebSettings.LayoutAlgorithm tidak didukung lagi di versi WebView baru.

Perhatian: API ini tidak didukung sama sekali di Android 4.4 dan yang lebih tinggi. Meskipun targetSdkVersion Anda disetel ke "18" atau yang lebih rendah, API ini tidak berpengaruh.

Anda dapat menangani perubahan ini dengan cara berikut:

  • Ubah gaya aplikasi Anda:

    Jika Anda memiliki kontrol terhadap HTML dan CSS pada halaman, Anda mungkin mendapati bahwa mengubah desain konten Anda mungkin merupakan pendekatan yang paling dapat diandalkan. Misalnya, untuk layar yang mencantumkan lisensi, Anda dapat menggabungkan teks dalam tag <pre>, yang dapat Anda lakukan dengan gaya berikut:

    <pre style="word-wrap: break-word; white-space: pre-wrap;">

    Hal ini dapat sangat membantu jika Anda belum menentukan properti area pandang untuk halaman Anda.

  • Gunakan algoritme tata letak TEXT_AUTOSIZING baru:

    Jika Anda menggunakan kolom sempit sebagai cara untuk membuat spektrum situs desktop yang lebih luas menjadi lebih mudah dibaca di perangkat seluler dan Anda tidak dapat mengubah konten HTML, algoritme TEXT_AUTOSIZING baru dapat menjadi alternatif yang sesuai untuk NARROW_COLUMNS.

Selain itu, nilai SINGLE_COLUMN—yang sebelumnya tidak digunakan lagi — juga tidak didukung lagi di versi baru WebView.

Menangani Peristiwa Sentuh di JavaScript

Jika halaman web Anda secara langsung menangani peristiwa sentuh di WebView, pastikan Anda juga menangani peristiwa touchcancel. Ada beberapa skenario di mana touchcancel akan dipanggil, yang dapat menyebabkan masalah jika tidak diterima:

  • Sebuah elemen disentuh (sehingga touchstart dan touchmove akan dipanggil) dan halaman di-scroll, akan menyebabkan touchcancel dimunculkan.
  • Sebuah elemen disentuh (touchstart dipanggil), tetapi event.preventDefault() tidak dipanggil, sehingga mengakibatkan touchcancel dimunculkan pada waktu yang tepat (sehingga WebView mengasumsikan bahwa Anda tidak ingin menggunakan peristiwa sentuh).