Android: 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

Nếu bạn muốn phân phối một ứng dụng web (hoặc chỉ là một trang web) như một phần của ứng dụng khách, bạn có thể thực hiện bằng cách sử dụng WebView. Lớp WebView là phần mở rộng của lớp View của  Android cho phép bạn hiển thị các trang web như là một phần của activity layout. Nó không bao gồm bất kỳ tính năng nào của trình duyệt web được phát triển đầy đủ, chẳng hạn như điều khiển điều hướng hoặc thanh địa chỉ. Tất cả những gì WebView làm, theo mặc định là hiển thị một trang web.

Một tình huống phổ biến trong đó việc sử dụng WebView là hữu ích là khi bạn muốn cung cấp thông tin trong ứng dụng của mình mà bạn có thể cần cập nhật, chẳng hạn như thỏa thuận người dùng cuối hoặc hướng dẫn sử dụng. Trong ứng dụng Android của bạn, bạn có thể tạo một Activity có chứa WebView, sau đó sử dụng nó để hiển thị tài liệu của bạn được lưu trữ trực tuyến.

Một kịch bản khác WebView có thể hữu dụng là ứng dụng của bạn cung cấp dữ liệu cho người dùng luôn yêu cầu kết nối Internet để truy xuất dữ liệu, chẳng hạn như email. Trong trường hợp này, bạn có thể thấy rằng việc xây dựng một WebView trong ứng dụng Android của mình để hiển thị một trang web có tất cả dữ liệu người dùng, sẽ hay hơn việc thực hiện một yêu cầu mạng, sau đó phân tích dữ liệu và hiển thị nó theo bố cục Android. Bạn có thể thiết kế một trang web phù hợp với thiết bị Android và sau đó triển khai ứng dụng WebView trong Android để tải trang web.

Bài viết này chỉ cho ta cách bắt đầu WebView và cách thực hiện một số điều bổ sung, chẳng hạn như xử lý điều hướng trang và liên kết JavaScript từ trang web của bạn với mã phía máy khách trong ứng dụng Android của bạn.

Thêm một WebView vào ứng dụng

Để thêm một WebView, ta có thể thành phần <WebView> vào activity layout, hoặc thiết lập toàn bộ cửa sổ Hoạt động như một WebView trong onCreate().

Thêm một WebView vào activity layout

Để thêm một WebView ta thêm đoạn mã sau vào tệp XML bố cục của activity:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

Để tải một trang web trong WebView, sử dụng loadUrl(). Ví dụ:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

Thêm một WebView trong onCreate ()

Để thêm WebView vào onCreate(), ta làm như sau:

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

Sau đó tải trang bằng:

myWebView.loadUrl("https://www.example.com");

Hoặc tải URL từ một chuỗi HTML:

// Tạo một chuỗi HTML dạng unencoded
// rồi chuyển thành dạng bytes, encode
// nó thành Base64, sau đó tải dữ liệu.
String unencodedHtml =
     "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

Lưu ý: Có thể vài hạn chế về đoạn HTML này. Hãy tìm hiểu thêm về loadData() và loadDataWithBaseURL() để biết thêm thông tin về các tùy chọn mã hóa.

Tất nhiên là trước khi hoạt động thì ứng dụng của ta phải có quyền truy cập Internet. Để có quyền truy cập internet, ta cần quyền INTERNET permision phép trong file manifest. Ví dụ:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

Đó là tất cả những gì ta cần để một WebView cơ bản hiển thị một trang web. Ngoài ra, ta có thể tùy chỉnh WebView bằng cách sửa đổi các mục sau:

  • Kích hoạt hỗ trợ toàn màn hình dùng WebChromeClient. Lớp này cũng được gọi khi WebView cần có quyền thay đổi giao diện người dùng của ứng dụng máy chủ, chẳng hạn như tạo hoặc đóng cửa sổ và gửi hộp thoại JavaScript cho người dùng. Để tìm hiểu thêm về gỡ lỗi trong ngữ cảnh này, hãy đọc Gỡ lỗi ứng dụng web.
  • Xử lý các sự kiện ảnh hưởng đến kết xuất nội dung, chẳng hạn như lỗi khi gửi biểu mẫu hoặc điều hướng với WebViewClient. Bạn cũng có thể sử dụng lớp con này để chặn tải URL.
  • Kích hoạt JavaScript bằng cách sửa đổi WebSettings.
  • Sử dụng JavaScript để truy cập các đối tượng khung Android mà bạn đã đưa vào WebView.

Làm việc với WebView trên các phiên bản Android cũ hơn

Để sử dụng an toàn các khả năng WebView mới hơn trên thiết bị mà ứng dụng của ta đang chạy, ta thêm AndroidX Webkit. Thư viện androidx.webkit là một thư viện tĩnh bạn có thể thêm vào ứng dụng của bạn để sử dụng các API android.webkit mà không có sẵn cho các phiên bản nền tảng cũ hơn.

Sử dụng JavaScript trong WebView

Nếu trang web có sử dụng JavaScript thì ta cần bật JavaScript cho WebView. Khi JavaScript được bật, ta cũng có thể tạo giao diện giữa mã ứng dụng và mã JavaScript.

Kích hoạt JavaScript

Theo mặc định thì JavaScript bị vô hiệu hóa trong WebView. Ta có thể kích hoạt nó thông qua tệp WebSettings được đính kèm với WebView. Bạn có thể truy xuất WebSettings bằng getSettings(), sau đó bật JavaScript bằng setJavaScriptEnabled().

Ví dụ:

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings cung cấp quyền truy cập vào một loạt các cài đặt khác mà bạn có thể thấy hữu ích. Ví dụ: nếu bạn đang phát triển một ứng dụng web được thiết kế dành riêng cho app WebView thì bạn có thể tạo chuỗi tác nhân người dùng tùy chỉnh bằng setUserAgentString(), sau đó truy vấn nó trong trang web của ta để xác minh rằng máy khách đang yêu cầu trang web của ta thực sự là ứng dụng Android.

Liên kết mã JavaScript với mã Android

Khi phát triển một ứng dụng web được thiết kế dành riêng cho WebView, ta có thể tạo giao diện giữa mã JavaScript và mã Android phía máy khách. Ví dụ như mã JavaScript của ta có thể gọi một phương thức trong mã Android của bạn để hiển thị Dialog  thay vì sử dụng hàm alert() của JavaScript .

Để liên kết một giao diện mới giữa mã JavaScript và Android ta gọi addJavascriptInterface(), chuyển cho nó một thể hiện lớp để liên kết với JavaScript và một tên giao diện mà JavaScript của ta có thể gọi để truy cập lớp.

Ví dụ: ta có thể đưa lớp sau vào ứng dụng Android:

public class WebAppInterface {
    Context mContext;

    /** Tạo thể hiện interface và thiết lập ngữ cảnh */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show một toast từ web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

Thận trọng: Nếu ta đã thiết lập phiên bản targetSdkVersion từ 17 trở lên thì ta phải thêm chú thích @JavascriptInterface vào bất kỳ phương thức nào ta muốn có cho JavaScript và phương thức này phải là public. Nếu không cung cấp chú thích thì trang web không thể truy cập phương thức này khi chạy trên Android 4.2 trở lên.

Trong ví dụ trên thì lớp WebAppInterface cho phép trang web tạo một thông báo Toast sử dụng phương thức showToast().

Ta có thể ràng buộc lớp này đến JavaScript chạy trong WebView với addJavascriptInterface() và đặt tên giao diện Android. Ví dụ:

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

Đoạn code trên sẽ tạo một interface  Android cho JavaScript chạy trong WebView. Tại thời điểm này, ứng dụng web của ta có quyền truy cập vào lớp WebAppInterface. Ví dụ dưới đây là một đoạn HTML và JavaScript tạo thông báo toast bằng giao diện mới khi người dùng nhấp vào nút:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

Không cần phải khởi tạo interface Android từ JavaScript, WebView tự động làm cho trang web của ta. Vì vậy, khi người dùng nhấp vào nút thì phương thức showAndroidToast() sẽ sử dụng interface Android để gọi phương thức  WebAppInterface.showToast().

Lưu ý: Đối tượng được liên kết với JavaScript của ta chạy trong một luồng khác chứ không phải trong luồng mà nó được xây dựng.

Thận trọng: Sử dụng addJavascriptInterface() cho phép JavaScript kiểm soát ứng dụng Android. Đây có thể là một tính năng rất hữu ích nhưng cũng gặp phải vấn đề bảo mật nguy hiểm. Khi HTML trong WebView không đáng tin cậy (ví dụ: một phần hoặc toàn bộ HTML được cung cấp bởi một người hoặc quy trình không xác định), thì kẻ tấn công có thể đưa mã HTML để thực thi mã phía máy khách của ta và có thể bất kỳ mã nào của kẻ tấn công chọn. Như vậy thì ta không nên sử dụng addJavascriptInterface() trừ khi ta đã viết tất cả mã HTML và JavaScript trong WebView. Ta cũng không nên cho phép người dùng điều hướng đến các trang web khác không phải của riêng ta trong phạm vi WebView. Thay vào đó, hãy cho phép ứng dụng trình duyệt mặc định của người dùng mở các liên kết bên ngoài theo mặc định, trình duyệt web của người dùng mở tất cả các liên kết URL, do đó, hãy cẩn thận nếu ta xử lý điều hướng trang như được mô tả trong phần sau).

Xử lý điều hướng trang

Khi người dùng nhấp vào một liên kết từ một trang web trong WebView, thì hành vi mặc định là Android sẽ khởi chạy một ứng dụng xử lý URL. Thông thường, trình duyệt web mặc định sẽ mở và tải URL đích. Tuy nhiên, bạn có thể ghi đè hành vi này cho WebView, vì vậy các liên kết mở trong WebView. Sau đó, ta có thể cho phép người dùng điều hướng backward và forward qua lịch sử trang web được duy trì bởi WebView.

Lưu ý: Vì lý do bảo mật, ứng dụng trình duyệt của hệ thống không chia sẻ dữ liệu ứng dụng của nó với ứng dụng của ta.

Để mở các liên kết được nhấp bởi người dùng, hãy cung cấp WebViewClient cho WebView bằng cách sử dụng setWebViewClient(). Ví dụ:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(MyWebViewClient);

Tất cả các liên kết người dùng nhấp sẽ được tải trong WebView.

Nếu bạn muốn kiểm soát nhiều hơn nơi tải liên kết được nhấp thì bạn hãy tạo WebViewClient riêng ghi đè phương thức shouldOverrideUrlLoading(). Ví dụ:

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if ("www.example.com".equals(Uri.parse(url).getHost())) {
            // This is my website, so do not override; let my WebView load the page
            return false;
        }
        // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
        return true;
    }
}

Sau đó tạo một thể hiện mới của WebViewClient cho WebView:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

Bây giờ khi người dùng nhấp vào một liên kết thì hệ thống sẽ gọi shouldOverrideUrlLoading() để kiểm tra xem máy chủ URL có khớp với một tên miền cụ thể không (như đã tạo ở trên). Nếu nó khớp thì phương thức sẽ trả về false để không ghi đè URL đang tải (nó cho phép WebView tải URL như bình thường). Nếu máy chủ URL không khớp thì một máy chủ Intent được tạo để khởi chạy Activity mặc định để xử lý URL (giải quyết theo trình duyệt web mặc định của người dùng).

Điều hướng lịch sử trang web

Khi WebView ghi đè URL đang tải thì nó sẽ tự động tích lũy lịch sử của các trang web đã truy cập. Bạn có thể điều hướng lùi và tiến qua lịch sử các phương thức goBack() và goForward().

Ví dụ: đoạn code sau đây cho thấy cách Activity có thể sử dụng nút Back của thiết bị để điều hướng lùi:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Nếu key sự kiện là nút Back và có lịch sử thì điều hướng lùi
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // Nếu không phải thì chuyển tới hành vi hệ thống mặc định (có thể là thoát activity)
    return super.onKeyDown(keyCode, event);
}

Phương thức canGoBack() trả về true nếu có thực sự lịch sử trang web là người sử dụng truy cập. Tương tự như vậy, ta có thể sử dụng canGoForward() để kiểm tra xem có một lịch sử chuyển tiếp hay không. Nếu ta không thực hiện kiểm tra này, thì một khi người dùng đã kết thúc lịch sử, thì goBack() hoặc goForward() sẽ không làm gì cả.

Xử lý thay đổi cấu hình thiết bị

Trong thời gian chạy, thay đổi trạng thái hoạt động xảy ra khi cấu hình của thiết bị thay đổi, chẳng hạn như khi người dùng xoay thiết bị hoặc loại bỏ trình chỉnh sửa phương thức nhập (IME). Những thay đổi này làm cho activity của một đối tượng WebView bị phá hủy và một activity mới được tạo ra, điều này cũng tạo ra một đối tượng WebView mới để tải URL của đối tượng bị phá hủy. Để sửa đổi hành vi mặc định của activity thì ta có thể thay đổi cách xử lý các thay đổi orientation trong tệp manifest.

Quản lý cửa sổ

Theo mặc định thì các yêu cầu mở cửa sổ mới được bỏ qua. Điều này đúng cho dù chúng được mở bằng JavaScript hoặc bằng thuộc tính đích trong một liên kết. Ta có thể tùy chỉnh WebChromeClient để cung cấp hành vi của riêng ta để mở nhiều cửa sổ.

Thận trọng: Để giữ cho ứng dụng an toàn hơn, tốt nhất ta nên ngăn chặn cửa sổ bật lên và cửa sổ mới mở. Cách an toàn nhất để thực hiện hành vi này là truyền "true"vào setSupportMultipleWindows() nhưng không ghi đè phương thức onCreateWindow(), điều này phụ thuộc vào setSupportMultipleWindows(). Lưu ý rằng logic này ngăn bất kỳ trang nào sử dụng target="_blank" trong quá trình tải các liên kết của nó.

Nguồn: https://developer.android.com/guide/webapps/webview

» Tiếp: Quản lý các đối tượng WebView
« Trước: Tổng quan về 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 !!!