Tạo ứng dụng web trong WebView

Dùng WebView để phân phối một ứng dụng web hoặc trang web như một phần của ứng dụng khách. Lớp WebView là một phần mở rộng của lớp View của Android để cho phép bạn sẽ hiển thị các trang web trong bố cục hoạt động của mình. Số liệu này không bao gồm của một trình duyệt web được phát triển hoàn chỉnh, chẳng hạn như các nút điều khiển điều hướng hoặc thanh địa chỉ. Theo mặc định, tất cả những gì WebView có thể làm là hiển thị một trang web.

WebView có thể giúp bạn cung cấp thông tin trong ứng dụng mà bạn có thể cần bản cập nhật mới, chẳng hạn như thoả thuận người dùng cuối hoặc hướng dẫn sử dụng. Trong ứng dụng Android, bạn có thể tạo Activity chứa WebView, rồi sử dụng bảng này để hiển thị tài liệu được lưu trữ trực tuyến.

WebView cũng có thể trợ giúp khi ứng dụng của bạn cung cấp dữ liệu cho người dùng 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 tạo WebView trong ứng dụng Android cho thấy một trang web sẽ dễ dàng hơn trang có tất cả dữ liệu người dùng, thay vì thực hiện yêu cầu mạng, thì phân tích cú pháp dữ liệu và hiển thị dữ liệu đó trong bố cục Android. Thay vào đó, bạn có thể thiết kế một trang web được thiết kế riêng cho các thiết bị chạy Android rồi triển khai WebView trong ứng dụng Android tải trang web.

Tài liệu này mô tả cách bắt đầu sử dụng WebView và cách liên kết JavaScript từ trang web của bạn đến mã phía máy khách trong ứng dụng Android của bạn, cách xử lý thao tác điều hướng trang và cách quản lý cửa sổ khi sử dụng WebView.

Tương thích với WebView trên các phiên bản Android cũ

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

Thêm vào tệp build.gradle như sau:

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

Groovy

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

Khám phá WebView ví dụ trên GitHub để biết thêm chi tiết.

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

Để thêm WebView vào ứng dụng, bạn có thể thêm phần tử <WebView> vào bố cục hoạt động hoặc đặt toàn bộ cửa sổ Activity dưới dạng WebView trong onCreate().

Thêm WebView trong bố cục hoạt động

Để thêm WebView vào ứng dụng trong bố cục, hãy thêm mã sau vào tệp XML bố cục của hoạt động:

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

Để tải một trang web trong WebView, hãy sử dụng loadUrl(), với tư cách là như trong ví dụ sau:

Kotlin

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

Java

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 ứng dụng của bạn bằng phương thức onCreate() của một hoạt động, hãy sử dụng logic tương tự như sau:

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

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

Sau đó, tải trang:

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

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

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

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

Ứng dụng của bạn phải có quyền truy cập Internet. Để truy cập Internet, hãy yêu cầu Quyền INTERNET trong tệp kê khai, như được thể hiện trong ví dụ sau:

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

Bạn có thể tuỳ chỉnh WebView bằng cách thực hiện thao tác bất kỳ sau đây:

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

Sử dụng JavaScript trong WebView

Nếu trang web bạn muốn tải trong WebView sử dụng JavaScript, bạn phải bật JavaScript cho WebView của bạn. Sau khi bật JavaScript, bạn có thể tạo giao diện giữa mã ứng dụng và mã JavaScript của bạn.

Bật JavaScript

Theo mặc định, JavaScript bị tắt trong WebView. Bạn có thể bật tính năng này thông qua Đã đính kèm WebSettings vào WebView của bạn. Truy xuất WebSettings bằng getSettings(), sau đó bật JavaScript với setJavaScriptEnabled().

Hãy xem ví dụ sau:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

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 nhiều chế độ cài đặt khác mà bạn có thể tìm 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 WebView trong ứng dụng Android, thì bạn có thể xác định một thuộc tính tuỳ chỉnh chuỗi tác nhân người dùng với setUserAgentString(), thì hãy truy vấn tác nhân người dùng tuỳ chỉnh trong trang web của bạn để xác minh rằng yêu cầu trang web là ứng dụng Android của bạn.

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ế riêng cho WebView trong ứng dụng Android, bạn có thể tạo giao diện giữa mã JavaScript và mã Android phía máy khách. Ví dụ: mã JavaScript 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 giao diện mới giữa JavaScript và mã Android, hãy gọi addJavascriptInterface()! truyền vào đó một thực thể lớp để liên kết với JavaScript và tên giao diện mà JavaScript của bạn có thể gọi để truy cập vào lớp này.

Ví dụ: bạn có thể thêm lớp sau đây vào ứng dụng Android:

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

Trong ví dụ này, lớp WebAppInterface cho phép trang web tạo một Toast, sử dụng showToast() .

Bạn có thể liên kết lớp này với JavaScript chạy trong WebView bằng addJavascriptInterface(), như trong ví dụ sau:

Kotlin

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

Java

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

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

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

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

Bạn không cần khởi chạy giao diện Android từ JavaScript. Chiến lược phát hành đĩa đơn WebView tự động cung cấp thư viện này cho trang web của bạn. Vì vậy, khi một người dùng nhấn vào nút này, hàm showAndroidToast() sẽ sử dụng giao diện Android để gọi phương thức WebAppInterface.showToast().

Xử lý thao tác điều hướng trang

Theo mặc định, khi người dùng nhấn vào một đường liên kết trên một trang web trong WebView, Android 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ở ra và tải URL đích. Tuy nhiên, bạn có thể ghi đè hành vi này cho WebView để các đường liên kết sẽ mở trong WebView. Sau đó, bạn có thể cho phép người dùng di chuyển lùi lại và tiến lên thông qua lịch sử trang web được duy trì theo WebView của bạn.

Để mở các đường liên kết mà người dùng nhấn vào, hãy cung cấp WebViewClient cho WebView của bạn đang sử dụng setWebViewClient(). Tất cả các đường liên kết mà người dùng nhấn vào đều tải trong WebView. Nếu bạn muốn kiểm soát nhiều hơn đối với khi đường liên kết được nhấp vào sẽ tải, hãy tạo WebViewClient của riêng bạn để ghi đè shouldOverrideUrlLoading() . Ví dụ sau giả định rằng MyWebViewClient là một lớp bên trong trong tổng số Activity.

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

Sau đó, hãy tạo một thực thể của WebViewClient mới này cho WebView:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

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

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

Xử lý URL tuỳ chỉnh

WebView áp dụng các quy định hạn chế khi yêu cầu tài nguyên và phân giải các đường liên kết sử dụng lược đồ URL tùy chỉnh. Ví dụ: nếu bạn triển khai lệnh gọi lại như shouldOverrideUrlLoading() hoặc shouldInterceptRequest(), thì WebView chỉ gọi chúng đối với các URL hợp lệ.

Ví dụ: WebView có thể không gọi phương thức shouldOverrideUrlLoading() cho các đường liên kết như sau:

<a href="showProfile">Show Profile</a>

Google sẽ xử lý các URL không hợp lệ (như URL trong ví dụ trước) không nhất quán trong WebView, vì vậy bạn nên sử dụng URL được định dạng đúng. Bạn có thể sử dụng lược đồ tuỳ chỉnh hoặc URL loại HTTPS cho miền mà tổ chức của bạn .

Thay vì sử dụng một chuỗi đơn giản trong một đường liên kết, như trong ví dụ trước, bạn có thể hãy sử dụng lược đồ tuỳ chỉnh, chẳng hạn như:

<a href="example-app:showProfile">Show Profile</a>

Sau đó, bạn có thể xử lý URL này trong phương thức shouldOverrideUrlLoading() như sau:

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

API shouldOverrideUrlLoading() chủ yếu dùng để khởi chạy ý định cho các URL cụ thể. Khi triển khai thẻ này, hãy nhớ trả về false cho URL WebView xử lý. Tuy nhiên, bạn không bị giới hạn ở việc khởi chạy ý định. Bạn có thể thay thế ý định khởi chạy bằng bất kỳ hành vi tuỳ chỉnh nào trong mã trước đó mẫu.

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

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

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

Nếu ứng dụng của bạn sử dụng AndroidX AppCompat 1.6.0 trở lên, bạn có thể đơn giản hoá trích đoạn nội dung khác:

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

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

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

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

Quản lý cửa sổ

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

Để giữ cho ứng dụng của bạn an toàn hơn, tốt nhất bạn 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 để triển khai hành vi này là chuyển "true" vào setSupportMultipleWindows() nhưng không ghi đè onCreateWindow()setSupportMultipleWindows() phụ thuộc vào. Logic này ngăn chặn bất kỳ sử dụng target="_blank" trong liên kết của trang đó khi tải.