Quản lý đối tượng WebView

Android cung cấp một số API để giúp bạn quản lý các đối tượng WebView hiển thị nội dung web trong ứng dụng của bạn.

Trang này mô tả cách sử dụng các API này để xử lý các đối tượng WebView hiệu quả hơn, giúp cải thiện độ ổn định và tính bảo mật của ứng dụng.

API phiên bản

Kể từ Android 7.0 (API cấp 24), người dùng có thể chọn trong số nhiều gói để hiển thị nội dung web trong đối tượng WebView. Thư viện AndroidX.webkit bao gồm phương thức getCurrentWebViewPackage() để tìm nạp thông tin liên quan đến gói đang hiển thị nội dung web trong ứng dụng của bạn. Phương thức này chỉ hữu ích khi phân tích các lỗi chỉ xảy ra khi ứng dụng của bạn cố gắng hiển thị nội dung trên web bằng cách sử dụng phương thức triển khai WebView của một gói cụ thể.

Để sử dụng phương thức này, hãy thêm logic có trong đoạn mã sau:

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

Dịch vụ Duyệt web An toàn của Google

Để mang lại trải nghiệm duyệt web an toàn hơn cho người dùng, các đối tượng WebView sẽ xác minh URL bằng tính năng Duyệt web An toàn của Google. Tính năng này cho phép ứng dụng của bạn hiển thị cảnh báo cho người dùng khi họ cố gắng truy cập một trang web có khả năng không an toàn.

Mặc dù giá trị mặc định của EnableSafeBrowsing là đúng, nhưng có một số trường hợp bạn có thể chỉ muốn bật tính năng Duyệt web an toàn theo điều kiện hoặc tắt tính năng này. Android 8.0 (API cấp 26) trở lên hỗ trợ việc sử dụng setSafeBrowsingEnabled() để bật/tắt tính năng Duyệt web an toàn cho từng đối tượng WebView riêng lẻ.

Nếu bạn muốn tất cả các đối tượng WebView chọn không kiểm tra tính năng Duyệt web an toàn, hãy thêm phần tử <meta-data> sau vào tệp kê khai của ứng dụng:

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

Xác định các hành động có lập trình

Khi một thực thể của WebView cố gắng tải một trang bị Google phân loại là mối đe doạ đã biết, theo mặc định, WebView sẽ hiển thị một quảng cáo xen kẽ cảnh báo người dùng về mối đe doạ đã biết. Màn hình này cho phép người dùng chọn vẫn tải URL hoặc quay lại trang trước đó một cách an toàn.

Nếu nhắm đến Android 8.1 (API cấp 27) trở lên, bạn có thể xác định bằng cách lập trình cách ứng dụng phản hồi một mối đe doạ đã biết theo những cách sau:

  • Bạn có thể kiểm soát việc ứng dụng của mình có báo cáo các mối đe doạ đã biết cho tính năng Duyệt web an toàn hay không.
  • Bạn có thể yêu cầu ứng dụng tự động thực hiện một thao tác cụ thể (chẳng hạn như quay lại trang an toàn) mỗi khi ứng dụng gặp một URL được phân loại là mối đe doạ đã biết.

Các đoạn mã sau đây cho biết cách hướng dẫn các thực thể WebView của ứng dụng luôn quay lại chế độ an toàn sau khi gặp phải mối đe doạ đã biết:

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

API vị trí địa lý HTML5

Đối với ứng dụng nhắm đến Android 6.0 (API cấp 23) trở lên, API vị trí địa lý chỉ được hỗ trợ trên các nguồn gốc bảo mật, chẳng hạn như HTTPS. Mọi yêu cầu truy cập vào API vị trí địa lý trên các nguồn gốc không an toàn sẽ tự động bị từ chối mà không cần gọi phương thức onGeolocationPermissionsShowPrompt() tương ứng.

Chọn không thu thập chỉ số

WebView có thể tải dữ liệu chẩn đoán ẩn danh lên Google khi người dùng đồng ý. Dữ liệu được thu thập theo từng ứng dụng đối với mỗi ứng dụng tạo thực thể cho WebView. Bạn có thể chọn không sử dụng tính năng này bằng cách tạo thẻ sau trong phần tử <application> của tệp kê khai:

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

Dữ liệu chỉ được tải lên qua ứng dụng nếu người dùng đồng ý ứng dụng không chọn không cho phép tải dữ liệu lên. Để biết thêm thông tin về cách chọn không sử dụng tính năng báo cáo dữ liệu chẩn đoán, hãy xem bài viết Quyền riêng tư của người dùng trong báo cáo WebView.

API Xử lý việc chấm dứt

API Xử lý chấm dứt xử lý các trường hợp trong đó quy trình kết xuất của đối tượng WebView biến mất, do hệ thống dừng trình kết xuất để lấy lại bộ nhớ cần thiết hoặc do quá trình kết xuất gặp sự cố. Bằng cách sử dụng API này, bạn sẽ cho phép ứng dụng của mình tiếp tục thực thi, ngay cả khi quy trình kết xuất đồ hoạ biến mất.

Nếu trình kết xuất gặp sự cố trong khi tải một trang web cụ thể, thì việc cố gắng tải lại trang đó có thể khiến đối tượng WebView mới biểu hiện cùng hành vi sự cố khi kết xuất.

Đoạn mã sau minh hoạ cách sử dụng API này trong một 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;
    }
}

API Mức độ quan trọng của trình kết xuất

Khi các đối tượng WebView hoạt động ở chế độ đa tiến trình, bạn có thể linh hoạt xử lý các tình huống hết bộ nhớ. Bạn có thể sử dụng API Mức độ quan trọng của trình kết xuất (ra mắt trong Android 8.0) để thiết lập chính sách mức độ ưu tiên cho trình kết xuất được chỉ định cho một đối tượng WebView cụ thể. Cụ thể, bạn có thể muốn phần chính của ứng dụng tiếp tục thực thi khi trình kết xuất hiển thị các đối tượng WebView của ứng dụng bị dừng. Chẳng hạn, bạn có thể thực hiện việc này, chẳng hạn như nếu bạn muốn không hiển thị đối tượng WebView trong một thời gian dài để hệ thống có thể lấy lại bộ nhớ mà trình kết xuất đang sử dụng.

Đoạn mã sau đây cho biết cách chỉ định mức độ ưu tiên cho quy trình kết xuất đồ hoạ liên kết với các đối tượng WebView của ứng dụng:

Kotlin

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

Java

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

Trong đoạn mã cụ thể này, mức độ ưu tiên của trình kết xuất giống với hoặc được liên kết với mức độ ưu tiên mặc định cho ứng dụng. Đối số true giảm mức độ ưu tiên của trình kết xuất xuống RENDERER_PRIORITY_WAIVED khi đối tượng WebView được liên kết không còn hiển thị nữa. Nói cách khác, đối số true cho biết ứng dụng của bạn không quan tâm đến việc hệ thống có duy trì hoạt động cho quy trình kết xuất hay không. Trên thực tế, mức độ ưu tiên thấp hơn này có thể khiến quá trình kết xuất bị tắt trong các tình huống hết bộ nhớ.

Để tìm hiểu thêm về cách hệ thống xử lý các tình huống dung lượng bộ nhớ thấp, hãy xem phần Các quy trình và vòng đời của ứng dụng.