Android: Quản lý các đối tượng WebView

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

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

Bài viết này mô tả cách sử dụng các API này để làm việc với các đối tượng WebView hiệu quả hơn, cải thiện tính ổn định và bảo mật ứng dụng của ta.

Phiên bản API

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

Để sử dụng phương thức này thì ta thêm logic được hiển thị trong đoạn code sau:

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

Lưu ý: Phương thức getCurrentWebViewPackage() có thể trả về nullnếu thiết bị đã được thiết lập không chính xác, không hỗ trợ sử dụng WebView (chẳng hạn như một thiết bị Wear OS), hoặc thiếu một  triển khai  WebView chưa được update, chẳng hạn như trên Android 4.4 (API level 19) và trước đó.

Dịch vụ duyệt web an toàn của Google

Để cung cấp cho người dùng trải nghiệm duyệt web an toàn hơn, các đối tượng WebView cần xác minh URL bằng Google Safe Browseing, cho phép ứng dụng của ta hiển thị cảnh báo cho người dùng khi họ cố điều hướng đến một trang web có khả năng không an toàn.

Mặc dù giá trị mặc định EnableSafeBrowsing là true, nhưng đôi khi ta chỉ muốn bật Duyệt web An toàn hoặc vô hiệu hóa nó. Các phiên bản Android 8.0 (API level 26) và cao hơn hỗ trợ bằng cách sử dụng  setSafeBrowsingEnabled() để chuyển đổi Duyệt web An toàn cho từng đối tượng WebView.

Nếu ta muốn rằng tất cả các đối tượng WebView đều bỏ qua kiểm tra Duyệt web An toàn, thì ta có thể thêm phần tử <meta-data> sau vào tệp manifest của ứng dụng:

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

 

Định nghĩa các hành động lập trình

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

Nếu ta nhắm mục tiêu là từ Android 8.1 (API level 27) trở lên thì ta có thể xác định cách lập trình để ứng dụng của ta đối phó với mối đe dọa đã biết:

  • Ta có thể kiểm soát xem ứng dụng của mình có báo cáo các mối đe dọa đã biết cho Duyệt web An toàn.
  • Ta có thể để ứng dụng của mình tự động thực hiện một hành động cụ thể, chẳng hạn như quay trở lại an toàn, mỗi khi nó gặp một URL được phân loại là một mối đe dọa đã biết.

Lưu ý: Để bảo vệ tối ưu trước các mối đe dọa đã biết, hãy đợi cho đến khi khởi tạo Duyệt web An toàn trước khi ta gọi phương thức loadUrl() của đối tượng WebView.

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

MyWebActivity.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", "Không cho phép khởi tạo Safe Browsing!");
                }
            }
        });
    }
}

MyWebViewClient.java

public class MyWebViewClient extends WebViewClientCompat {
    // Tự động đi tới "back to safety" khi cố tải một website mà
    // Google đã nhận diện là mối đe dọa đã biết. Một thể hiện của WebView sẽ gọi
    // phương thức này chỉ sau khi Safe Browsing được khởi tạo, do vậy không cần
    // điều kiện logic nào ở đây.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponseCompat callback) {
        // Đối số "true" chỉ ra rằng app của ta sẽ thông báo sự cố
        // tương ứng tới 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 định vị địa lý HTML5

Đối với các ứng dụng nhắm mục tiêu từ Android 6.0 (API level 23) trở lên, API vị trí địa lý sẽ chỉ được hỗ trợ nếu đảm bảo tính an toàn, chẳng hạn như HTTPS. Mọi yêu cầu đối với API vị trí địa lý về 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 .

Không thực hiện thu thập số liệu

WebView có khả năng 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 trên cơ sở mỗi ứng dụng đã khởi tạo một WebView. Bạn có thể từ chối tính năng này bằng cách tạo thẻ sau trong phần tử <application> của tệp manifest:

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

Dữ liệu sẽ chỉ được tải lên từ một ứng dụng nếu người dùng đã đồng ý  ứng dụng không được chọn.

API xử lý chấm dứt

API này xử lý các trường hợp trong đó quá trình kết xuất cho một đối tượng WebView biến mất, vì hệ thống đã hủy trình kết xuất để lấy lại bộ nhớ rất cần thiết hoặc do chính quá trình kết xuất bị lỗi. Bằng cách sử dụng API này, ta cho phép ứng dụng của mình tiếp tục thực thi, mặc dù quá trình kết xuất đã biến mất.

Thận trọng: Nếu ứng dụng tiếp tục thực thi sau khi quá trình kết xuất biến mất, thì thể hiện tương ứng của WebView không thể được sử dụng lại, bất kể quá trình kết xuất bị hủy hay bị treo. Ứng dụng của ta phải xóa phiên bản khỏi hệ thống phân cấp chế độ xem và hủy phiên bản để tiếp tục thực thi. Sau đó, ứng dụng phải tạo một thể hiện hoàn toàn mới của WebView để tiếp tục kết xuất các trang web.

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

Đoạn mã sau minh họa cách sử dụng API này:

public class MyRendererTrackingWebViewClient extends WebViewClient {
    private WebView mWebView;

    @Override
    public boolean onRenderProcessGone(WebView view,
            RenderProcessGoneDetail detail) {
        if (!detail.didCrash()) {
            // Trình kết xuất bị hủy vì hệ thống chạy ngoài bộ nhớ.
            // App có thể khôi phục lại tương đối tốt bằng cách tạo
            // một thể hiện mới của WebView tại foreground.
            Log.e("MY_APP_TAG", "Hệ thống đã hủy việc WebView kết xuất xử lý " +
                    "để thu thập lại vùng nhớ. Đang tạo lại...");

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

            // Tại đây, biến thể hiện "mWebView" được bảo vệ
            // bằng việc gán null, cho nên nó an toàn để khởi tạo lại.

            return true; // Tiếp tục thực thi app.
        }

        // Trình kết xuất bị lỗi do lỗi nội bộ (internal), chẳng hạn như
        // bộ nhớ vi phạm quyền truy cập.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!");

        // Trong ví dụ này, bản thân app gặp sự cố sau khi phát hiện ra
        // trình kết xuất bị lỗi. Nếu ta chọn xử lý sự cố mềm mại hơn
        // và cho phép app tiếp tục thực thi thì ta nên 1) hủy
        // thể hiện WebView hiện thời, 2) xác định logic cách app có thể
        // tiếp tục thực thi, và 3) trả về "true".
        return false;
    }
}

API quan trọng của trình kết xuất

Bây giờ, khi các đối tượng WebView hoạt động ở chế độ đa xử lý, ta có thể linh hoạt trong cách ứng dụng của ta xử lý các tình huống hết bộ nhớ. Ta có thể sử dụng API Trình kết xuất Quan trọng, được giới thiệu trong Android 8.0, để đặt chính sách ưu tiên cho trình kết xuất được gán cho một đối tượng WebView cụ thể. Cụ thể, ta 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ị hủy. Ta có thể làm điều này, ví dụ như nếu ta muốn không hiển thị đối tượng WebView trong một khoảng 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 ta cách gán mức độ ưu tiên cho quy trình kết xuất được liên kết với các đối tượng WebView của ứng dụng:

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

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

Cảnh báo: Để duy trì sự ổn định của ứng dụng, ta không nên thay đổi chính sách ưu tiên trình kết xuất cho một đối tượng WebView trừ khi bạn cũng sử dụng API Xử lý ngắt để chỉ định cách WebView phản ứng khi trình kết xuất được liên kết của nó biến mất.

Để tìm hiểu thêm về cách hệ thống xử lý các tình huống bộ nhớ thấp, hãy xem Quy trình và Vòng đời ứng dụng.

» Tiếp: Quyền riêng tư của người dùng trong báo cáo WebView
« Trước: Xây dựng ứng dụng web trong WebView
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!