Mengelola objek WebView

Android menyediakan sejumlah API untuk membantu Anda mengelola objek WebView yang menampilkan konten web dalam aplikasi Anda.

Halaman ini menjelaskan cara menggunakan API ini untuk bekerja dengan objek WebView secara lebih efektif, meningkatkan stabilitas dan keamanan aplikasi Anda.

Versi API

Mulai Android 7.0 (API level 24), pengguna bisa memilih di antara beberapa paket berbeda untuk menampilkan konten web dalam objek WebView. Android 8.0 (API level 26) dan versi yang lebih baru menyertakan API untuk mengambil informasi yang terkait dengan paket yang menampilkan konten web dalam aplikasi Anda. API ini sangat berguna ketika menganalisis error yang hanya terjadi jika aplikasi Anda mencoba menampilkan konten web menggunakan implementasi paket tertentu dari WebView.

Untuk menggunakan API ini, tambahkan logika yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

val webViewPackageInfo = WebView.getCurrentWebViewPackage()
Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")

Java

PackageInfo webViewPackageInfo = WebView.getCurrentWebViewPackage();
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);

Catatan: Metode WebView.getCurrentWebViewPackage() bisa menunjukkan null jika perangkat belum dipersiapkan dengan benar. Metode tersebut juga menunjukkan null jika Anda menjalankan aplikasi di perangkat yang tidak mendukung WebView, seperti perangkat Wear OS.

Google Safe Browsing API

Untuk memberikan pengalaman menjelajah yang lebih aman bagi pengguna, object WebView akan memverifikasi URL menggunakan Google Safe Browsing, yang memungkinkan aplikasi Anda menampilkan peringatan kepada pengguna saat mencoba menuju ke situs yang berpotensi tidak aman.

Sedangkan nilai default EnableSafeBrowsing adalah true, terkadang ada kasus mungkin Anda hanya ingin mengaktifkan Safe Browsing secara kondisional atau menonaktifkannya. Android 8.0 (API level 26) dan lebih tinggi mendukung menggunakan setSafeBrowsingEnabled(). Aplikasi yang mengompilasi di API level lebih rendah tidak bisa menggunakan setSafeBrowsingEnabled() dan harus mengubah nilai EnableSafeBrowsing di manifes untuk menonaktifkan fitur untuk semua instance WebView.

Jika aplikasi Anda menargetkan Android (API Level 25) atau lebih rendah, Anda bisa memilih untuk tidak menyertakan object WebView dari pemeriksaan URL pada daftar situs tidak aman Google Safe Browsing dengan menambahkan elemen <meta-data> berikut ke file manifes aplikasi Anda:

<manifest>
    <application>
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="false" />
        ...
    </application>
</manifest>

Menentukan tindakan terprogram

Ketika sebuah instance WebView mencoba memuat halaman yang telah diklasifikasikan oleh Google sebagai ancaman yang diketahui, WebView secara default akan menunjukkan pengantara yang memperingatkan pengguna dari ancaman yang diketahui tersebut. Layar ini memberi pengguna opsi untuk tetap memuat URL atau kembali ke halaman sebelumnya yang aman.

Bila Anda menargetkan Android 8.1 (API level 27) atau yang lebih tinggi, Anda bisa menentukan lewat program bagaimana aplikasi merespons ancaman yang diketahui:

  • Anda bisa mengontrol apakah aplikasi Anda melaporkan ancaman yang diketahui ke Safe Browsing.
  • Anda bisa mengatur agar aplikasi secara otomatis melakukan aksi tertentu—seperti kembali ke halaman aman—setiap kali menemukan URL yang diklasifikasikan sebagai ancaman yang diketahui.

Catatan: Untuk perlindungan optimal terhadap ancaman yang diketahui, tunggu hingga Anda telah melakukan inisialiasi Safe Browsing sebelum Anda mengaktifkan objek WebView metode loadUrl().

Cuplikan kode berikut menunjukkan bagaimana Anda bisa menginstruksikan instance WebView aplikasi untuk selalu kembali ke halaman aman setelah menjumpai ancaman yang diketahui:

MyWebActivity.java

Kotlin

private lateinit var superSafeWebView: WebView
private var safeBrowsingIsInitialized: Boolean = false

// ...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    superSafeWebView = WebView(this)
    superSafeWebView.webViewClient = MyWebViewClient()
    safeBrowsingIsInitialized = false

    superSafeWebView.startSafeBrowsing(this, ValueCallback<Boolean> { success ->
        safeBrowsingIsInitialized = true
        if (!success) {
            Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
        }
    })
}

Java

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    superSafeWebView.startSafeBrowsing(this, new ValueCallback<Boolean>() {
        @Override
        public void onReceiveValue(Boolean success) {
            safeBrowsingIsInitialized = true;
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
            }
        }
    });
}

MyWebViewClient.java

Kotlin

class MyWebViewClient : WebViewClient() {
    // Automatically go "back to safety" when attempting to load a website that
    // Google has identified as a known threat. An instance of WebView calls
    // this method only after Safe Browsing is initialized, so there's no
    // conditional logic needed here.
    override fun onSafeBrowsingHit(
            view: WebView,
            request: WebResourceRequest,
            threatType: Int,
            callback: SafeBrowsingResponse
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true)
        Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
    }
}

Java

public class MyWebViewClient extends WebViewClient {
    // Automatically go "back to safety" when attempting to load a website that
    // Google has identified as a known threat. An instance of WebView calls
    // this method only after Safe Browsing is initialized, so there's no
    // conditional logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponse callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true);
        Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                Toast.LENGTH_LONG).show();
    }
}

HTML5 Geolocation API

Untuk aplikasi yang menargetkan Android 6.0 (API level 23) dan lebih tinggi, Geolocation API hanya didukung pada asal yang aman, seperti HTTPS. Setiap permintaan ke Geolocation API pada asal yang tidak aman akan secara otomatis ditolak tanpa meminta metode onGeolocationPermissionsShowPrompt() yang sesuai.

Tidak mengikuti koleksi metrik

WebView memiliki kemampuan mengupload data diagnostik anonim ke Google jika pengguna telah memberikan persetujuannya. Data dikumpulkan pada basis per-aplikasi untuk setiap aplikasi yang membuat instance WebView. Anda bisa memilih untuk tidak ikut fitur ini dengan membuat tag berikut di elemen <application> manifes.

<manifest>
    <application>
    ...
    <meta-data android:name="android.webkit.WebView.MetricsOptOut"
               android:value="true" />
    </application>
</manifest>

Data hanya akan diupload dari aplikasi jika pengguna telah menyetujui dan aplikasinya tidak ditolak.

Termination Handling API

API ini menangani kasus proses renderer untuk objek WebView terhenti, baik karena sistem menutup renderer untuk mendapatkan kembali memori yang sangat dibutuhkan atau karena proses renderer itu sendiri yang error. Dengan menggunakan API ini, Anda mengizinkan aplikasi untuk terus berjalan, meskipun proses renderer telah terhenti.

Perhatian: Jika aplikasi Anda terus berjalan setelah proses render terhenti, instance WebView yang terkait tidak dapat digunakan kembali, baik itu karena proses renderer ditutup atau tidak bekerja. Aplikasi Anda harus menghapus instance dari hierarki tampilan dan menutup instance untuk melanjutkan eksekusi. Kemudian aplikasi Anda harus membuat instance WebView yang sepenuhnya baru untuk melanjutkan perenderan halaman web.

Ketahuilah bahwa, jika renderer tidak bekerja ketika memuat halaman web tertentu, mencoba memuat halaman yang sama bisa menyebabkan objek WebView baru memunculkan perilaku error perenderan yang sama.

Cuplikan kode berikut menggambarkan cara menggunakan API ini:

Kotlin

inner class MyRendererTrackingWebViewClient : WebViewClient() {
    private var mWebView: WebView? = null

    override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
        if (!detail.didCrash()) {
            // Renderer was killed because the system ran out of memory.
            // The app can recover gracefully by creating a new WebView instance
            // in the foreground.
            Log.e("MY_APP_TAG", ("System killed the WebView rendering process " +
                "to reclaim memory. Recreating..."))

            mWebView?.also { webView ->
                val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container)
                webViewContainer.removeView(webView)
                webView.destroy()
                mWebView = null
            }

            // By this point, the instance variable "mWebView" is guaranteed
            // to be null, so it's safe to reinitialize it.

            return true // The app continues executing.
        }

        // Renderer crashed because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!")

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you choose to handle the crash more gracefully
        // and allow your app to continue executing, you should 1) destroy the
        // current WebView instance, 2) specify logic for how the app can
        // continue executing, and 3) return "true" instead.
        return false
    }
}

Java

public class MyRendererTrackingWebViewClient extends WebViewClient {
    private WebView mWebView;

    @Override
    public boolean onRenderProcessGone(WebView view,
            RenderProcessGoneDetail detail) {
        if (!detail.didCrash()) {
            // Renderer was killed because the system ran out of memory.
            // The app can recover gracefully by creating a new WebView instance
            // in the foreground.
            Log.e("MY_APP_TAG", "System killed the WebView rendering process " +
                    "to reclaim memory. Recreating...");

            if (mWebView != null) {
                ViewGroup webViewContainer =
                        (ViewGroup) findViewById(R.id.my_web_view_container);
                webViewContainer.removeView(mWebView);
                mWebView.destroy();
                mWebView = null;
            }

            // By this point, the instance variable "mWebView" is guaranteed
            // to be null, so it's safe to reinitialize it.

            return true; // The app continues executing.
        }

        // Renderer crashed because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!");

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you choose to handle the crash more gracefully
        // and allow your app to continue executing, you should 1) destroy the
        // current WebView instance, 2) specify logic for how the app can
        // continue executing, and 3) return "true" instead.
        return false;
    }
}

Renderer Importance API

Sekarang setelah objek WebView beroperasi dalam mode multiproses, Anda memiliki fleksibilitas mengenai bagaimana aplikasi Anda menangani situasi memori-habis. Anda bisa menggunakan Renderer Importance API, yang diperkenalkan pada Android 8.0, untuk menyetel kebijakan prioritas bagi renderer yang ditetapkan untuk objek WebView tertentu. Khususnya, Anda mungkin menginginkan bagian utama aplikasi Anda terus berjalan ketika renderer yang menampilkan objek WebView aplikasi Anda terhenti. Anda mungkin melakukannya, misalnya, jika Anda berharap untuk tidak menampilkan objek WebView dalam jangka waktu yang lama sehingga sistem bisa mendapatkan kembali memori yang digunakan renderer.

Cuplikan kode berikut menunjukkan cara menetapkan prioritas ke proses renderer yang terkait dengan objek WebView aplikasi Anda:

Kotlin

val myWebView: WebView = ...
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)

Java

WebView myWebView;
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);

Dalam cuplikan khusus ini, prioritas renderer sama dengan (atau "terikat ke") prioritas default untuk aplikasi. Argumen true menurunkan prioritas renderer menjadi RENDERER_PRIORITY_WAIVED ketika objek WebView yang terkait tidak lagi terlihat. Dengan kata lain, argumen true menunjukkan bahwa aplikasi Anda tidak peduli jika sistem membuat proses renderer tetap aktif. Bahkan, tingkat prioritas yang lebih rendah ini memungkinkan proses renderer ditutup ketika terjadi situasi memori-habis.

Peringatan: Demi menjaga stabilitas aplikasi, Anda tidak boleh mengubah kebijakan prioritas renderer untuk objek WebView kecuali Anda juga menggunakan Termination Handle API untuk menentukan bagaimana WebView bereaksi saat renderer terkaitnya terhenti.

Untuk mempelajari lebih lanjut tentang bagaimana sistem menangani situasi memori-rendah, lihat Siklus Hidup Aplikasi dan Pemrosesan.