Mengelola objek WebView

Android menyediakan beberapa API untuk membantu Anda mengelola objek WebView yang menampilkan konten web di aplikasi Anda.

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

Version API

Mulai Android 7.0 (API level 24), pengguna dapat memilih di antara beberapa paket berbeda untuk menampilkan konten web di objek WebView. Library AndroidX.webkit menyertakan metode getCurrentWebViewPackage() untuk mengambil informasi terkait paket yang menampilkan konten web di aplikasi Anda. Metode ini berguna saat menganalisis error yang hanya terjadi saat aplikasi Anda mencoba menampilkan konten web menggunakan implementasi paket tertentu dari WebView.

Untuk menggunakan metode ini, tambahkan logika yang ditampilkan dalam cuplikan kode berikut:

Kotlin

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

Java

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

Layanan Google Safe Browsing

Untuk memberi pengguna pengalaman penjelajahan yang lebih aman, objek WebView memverifikasi URL menggunakan Google Safe Browsing, yang memungkinkan aplikasi Anda menampilkan peringatan kepada pengguna saat mereka mencoba membuka situs yang berpotensi tidak aman.

Meskipun nilai default EnableSafeBrowsing adalah true, ada kasus saat Anda mungkin hanya ingin mengaktifkan Safe Browsing secara kondisional atau menonaktifkannya. Android 8.0 (API level 26) dan yang lebih baru mendukung penggunaan setSafeBrowsingEnabled() untuk beralih Safe Browsing untuk setiap objek WebView.

Jika Anda ingin semua objek WebView tidak diikutsertakan dalam pemeriksaan Safe Browsing, tambahkan 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

Saat instance WebView mencoba memuat halaman yang diklasifikasikan oleh Google sebagai ancaman yang diketahui, WebView secara default akan menampilkan interstisial yang memperingatkan pengguna tentang ancaman yang diketahui tersebut. Layar ini memberikan opsi kepada pengguna untuk tetap memuat URL atau kembali ke halaman sebelumnya yang aman.

Jika menargetkan Android 8.1 (API level 27) atau yang lebih baru, Anda dapat menentukan cara aplikasi merespons ancaman yang diketahui secara terprogram dengan cara berikut:

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

Cuplikan kode berikut menunjukkan cara menginstruksikan instance WebView aplikasi Anda untuk selalu kembali ke tempat aman setelah mengalami 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

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.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;

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.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 : WebViewClientCompat() {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies 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: SafeBrowsingResponseCompat
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true)
            Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
        }
    }
}

Java

public class MyWebViewClient extends WebViewClientCompat {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies 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, SafeBrowsingResponseCompat callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            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 yang lebih baru, Geolocation API hanya didukung di origin yang aman, seperti HTTPS. Setiap permintaan ke Geolocation API pada asal yang tidak aman akan otomatis ditolak tanpa memanggil metode onGeolocationPermissionsShowPrompt() yang sesuai.

Memilih tidak ikut pengumpulan metrik

WebView memiliki kemampuan untuk mengupload data diagnostik anonim ke Google saat pengguna memberikan izin. Data dikumpulkan per aplikasi untuk setiap aplikasi yang membuat instance WebView. Anda dapat memilih untuk tidak menggunakan 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 diupload dari aplikasi jika pengguna mengizinkan dan aplikasi tidak memilih untuk tidak ikut. Untuk mengetahui informasi selengkapnya tentang cara memilih tidak ikut pelaporan data diagnostik, lihat Privasi pengguna dalam pelaporan WebView.

Termination Handling API

Penghentian Handling API menangani kasus saat proses perender untuk objek WebView hilang, baik karena sistem mematikan perender untuk mendapatkan kembali memori yang diperlukan atau karena proses perender mengalami error. Dengan menggunakan API ini, Anda mengizinkan aplikasi terus berjalan, meskipun proses perender berhenti.

Jika perender mengalami error saat memuat halaman web tertentu, mencoba memuat halaman yang sama lagi dapat menyebabkan objek WebView baru menampilkan perilaku error rendering yang sama.

Cuplikan kode berikut mengilustrasikan cara menggunakan API ini dalam Activity:

Kotlin

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

    override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
        if (!detail.didCrash()) {
            // Renderer is 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 crashes 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 handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // 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 is 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 crashes 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 handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false;
    }
}

Renderer Importance API

Saat objek WebView beroperasi dalam mode multiproses, Anda memiliki beberapa fleksibilitas dalam cara aplikasi menangani situasi kehabisan memori. Anda dapat menggunakan Renderer Importance API, yang diperkenalkan di Android 8.0, untuk menetapkan kebijakan prioritas bagi perender yang ditetapkan ke objek WebView tertentu. Secara khusus, Anda mungkin ingin bagian utama aplikasi terus berjalan saat perender yang menampilkan objek WebView aplikasi telah dihentikan. Anda mungkin melakukannya, misalnya, jika berharap untuk tidak menampilkan objek WebView dalam waktu lama sehingga sistem dapat memperoleh kembali memori yang digunakan perender.

Cuplikan kode berikut menunjukkan cara menetapkan prioritas ke proses perender 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 perender sama dengan—atau terikat dengan—prioritas default untuk aplikasi. Argumen true menurunkan prioritas perender menjadi RENDERER_PRIORITY_WAIVED saat objek WebView terkait tidak lagi terlihat. Dengan kata lain, argumen true menunjukkan bahwa aplikasi Anda tidak peduli apakah sistem menjaga proses perender tetap aktif atau tidak. Bahkan, tingkat prioritas yang lebih rendah ini mungkin membuat proses perender dihentikan dalam situasi kehabisan memori.

Untuk mempelajari lebih lanjut cara sistem menangani situasi memori rendah, lihat Proses dan siklus proses aplikasi.