Tải nội dung trong ứng dụng

Bạn có thể cung cấp nội dung dựa trên nền tảng web (chẳng hạn như HTML, JavaScript và CSS—để ứng dụng của bạn sử dụng và bạn biên dịch tĩnh thành ứng dụng thay vì hơn là tìm nạp qua Internet.

Nội dung trong ứng dụng không yêu cầu truy cập Internet hay tiêu tốn băng thông của người dùng. Nếu nội dung chỉ được thiết kế riêng cho WebView, tức là nội dung đó phụ thuộc vào việc giao tiếp với một ứng dụng gốc—thì người dùng không thể vô tình hãy tải tệp đó trên trình duyệt web.

Tuy nhiên, nội dung trong ứng dụng có một số hạn chế. Cập nhật nội dung dựa trên nền tảng web yêu cầu vận chuyển một bản cập nhật ứng dụng mới và có khả năng sản phẩm không khớp giữa nội dung trên trang web và nội dung trong ứng dụng trên thiết bị. người dùng có phiên bản ứng dụng lỗi thời.

WebViewAssetLoader

WebViewAssetLoader là một tải nội dung trong ứng dụng theo cách linh hoạt và hiệu quả WebView. Lớp này hỗ trợ sau:

  • Tải nội dung bằng URL HTTP(S) để có khả năng tương thích với cùng nguồn gốc .
  • Tải các tài nguyên phụ như JavaScript, CSS, hình ảnh và iframe.

Đưa WebViewAssetLoader vào tệp hoạt động chính của bạn. Sau đây là một ví dụ về cách tải nội dung web đơn giản từ thư mục thành phần:

Kotlin

private class LocalContentWebViewClient(private val assetLoader: WebViewAssetLoader) : WebViewClientCompat() {
    @RequiresApi(21)
    override fun shouldInterceptRequest(
        view: WebView,
        request: WebResourceRequest
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(request.url)
    }

    // To support API < 21.
    override fun shouldInterceptRequest(
        view: WebView,
        url: String
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(Uri.parse(url))
    }
}

Java

private static class LocalContentWebViewClient extends WebViewClientCompat {

    private final WebViewAssetLoader mAssetLoader;

    LocalContentWebViewClient(WebViewAssetLoader assetLoader) {
        mAssetLoader = assetLoader;
    }

    @Override
    @RequiresApi(21)
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     WebResourceRequest request) {
        return mAssetLoader.shouldInterceptRequest(request.getUrl());
    }

    @Override
    @SuppressWarnings("deprecation") // To support API < 21.
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     String url) {
        return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
    }
}

Ứng dụng phải định cấu hình một thực thể WebViewAssetLoader cho phù hợp với nhu cầu. Chiến lược phát hành đĩa đơn phần tiếp theo có ví dụ.

Tạo thành phần và tài nguyên trong ứng dụng

WebViewAssetLoader dựa vào PathHandler để tải tài nguyên tương ứng với một đường dẫn tài nguyên nhất định. Mặc dù bạn có thể triển khai giao diện này để truy xuất tài nguyên theo yêu cầu của ứng dụng, Gói thư viện Webkit AssetsPathHandlerResourcesPathHandler để tải các thành phần và tài nguyên Android tương ứng.

Để bắt đầu, hãy tạo các thành phần và tài nguyên cho ứng dụng của bạn. Nhìn chung, những điều sau sẽ áp dụng:

  • Các tệp văn bản như HTML, JavaScript và CSS đều thuộc về các thành phần.
  • Hình ảnh và các tệp nhị phân khác thuộc tài nguyên.

Để thêm tệp web dạng văn bản vào một dự án, hãy làm như sau:

  1. Trong Android Studio, hãy nhấp chuột phải vào app > (ứng dụng) > src > thư mục main rồi chọn Mới > Thư mục.
    Hình ảnh cho thấy các trình đơn thư mục tạo của Android Studio
    Hình 1. Tạo thư mục thành phần cho dự án.
  2. Đặt tên cho thư mục là "Asset" (tài sản).
    Hình ảnh cho thấy thư mục thành phần
    Hình 2. Đặt tên cho thư mục thành phần.
  3. Nhấp chuột phải vào thư mục Asset rồi nhấp vào New > (Mới >) Tệp. Nhập index.html rồi nhấn phím Return hoặc Phím Enter.
  4. Lặp lại bước trước đó để tạo một tệp trống cho stylesheet.css.
  5. Điền nội dung vào hai mã tiếp theo vào các tệp trống đã tạo mẫu.
```html
<!-- index.html content -->

<html>
  <head>
    <!-- Tip: Use relative URLs when referring to other in-app content to give
              your app code the flexibility to change the scheme or domain as
              necessary. -->
    <link rel="stylesheet" href="/assets/stylesheet.css">
  </head>
  <body>
    <p>This file is loaded from in-app content.</p>
    <p><img src="/res/drawable/android_robot.png" alt="Android robot" width="100"></p>
  </body>
</html>
```

```css
<!-- stylesheet.css content -->

body {
  background-color: lightblue;
}
```

Để thêm tệp web dựa trên hình ảnh vào dự án, hãy làm như sau:

  1. Tải xuống Android_symbol_green_RGB.png vào máy cục bộ của bạn.

  2. Đổi tên tệp thành android_robot.png.

  3. Di chuyển tệp vào thư mục main/res/drawable của dự án theo cách thủ công trên ổ đĩa cứng của bạn.

Hình 4 cho thấy hình ảnh bạn đã thêm và văn bản từ các mã mẫu trước đó hiển thị trong ứng dụng.

Hình ảnh cho thấy kết quả kết xuất trong ứng dụng
Hình 4. Tệp HTML trong ứng dụng và tệp hình ảnh hiển thị trong ứng dụng.

Để hoàn tất ứng dụng này, hãy làm như sau:

  1. Đăng ký trình xử lý và định cấu hình AssetLoader bằng cách thêm phương thức sau đây vào phương thức onCreate():

    Kotlin

    val assetLoader = WebViewAssetLoader.Builder()
                           .addPathHandler("/assets/", AssetsPathHandler(this))
                           .addPathHandler("/res/", ResourcesPathHandler(this))
                           .build()
    webView.webViewClient = LocalContentWebViewClient(assetLoader)
    

    Java

    final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
             .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
             .addPathHandler("/res/", new WebViewAssetLoader.ResourcesPathHandler(this))
             .build();
    mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
    
  2. Tải nội dung bằng cách thêm mã sau vào phương thức onCreate():

    Kotlin

    webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
    

    Java

    mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
    

Kết hợp nội dung trong ứng dụng với tài nguyên từ trang web của bạn

Ứng dụng của bạn có thể cần tải kết hợp cả nội dung trong ứng dụng và nội dung từ Internet, chẳng hạn như trang HTML trong ứng dụng do CSS của trang web tạo kiểu. WebViewAssetLoader hỗ trợ trường hợp sử dụng này. Nếu không có Các thực thể PathHandler có thể tìm thấy tài nguyên cho đường dẫn đã cho, WebView sẽ rơi quay lại để tải nội dung từ Internet. Nếu bạn kết hợp nội dung trong ứng dụng với tài nguyên từ trang web của bạn, đặt trước đường dẫn thư mục, chẳng hạn như /assets/ hoặc /resources/ đối với tài nguyên trong ứng dụng. Tránh lưu trữ bất kỳ tài nguyên nào trong ở các vị trí đó.

Kotlin

val assetLoader = WebViewAssetLoader.Builder()
                        .setDomain("example.com") // Replace this with your website's domain.
                        .addPathHandler("/assets/", AssetsPathHandler(this))
                        .build()

webView.webViewClient = LocalContentWebViewClient(assetLoader)
val inAppHtmlUrl = "https://example.com/assets/index.html"
webView.loadUrl(inAppHtmlUrl)
val websiteUrl = "https://example.com/website/data.json"

// JavaScript code to fetch() content from the same origin.
val jsCode = "fetch('$websiteUrl')" +
        ".then(resp => resp.json())" +
        ".then(data => console.log(data));"

webView.evaluateJavascript(jsCode, null)

Java

final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
           .setDomain("example.com") // Replace this with your website's domain.
           .addPathHandler("/assets/", new AssetsPathHandler(this))
           .build();

mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
String inAppHtmlUrl = "https://example.com/assets/index.html";
mWebView.loadUrl(inAppHtmlUrl);
String websiteUrl = "https://example.com/website/data.json";

// JavaScript code to fetch() content from the same origin.
String jsCode = "fetch('" + websiteUrl + "')" +
      ".then(resp => resp.json())" +
      ".then(data => console.log(data));";

mWebView.evaluateJavascript(jsCode, null);

Xem bản minh hoạ WebView trên GitHub ví dụ về trang HTML trong ứng dụng tìm nạp dữ liệu JSON được lưu trữ trên web.

loadDataWithBaseURL

Khi ứng dụng của bạn chỉ cần tải một trang HTML và không cần chặn các nguồn phụ, hãy cân nhắc sử dụng loadDataWithBaseURL()! mà không cần phải có thành phần ứng dụng. Bạn có thể sử dụng như trong đoạn mã sau mẫu:

Kotlin

val html = "<html><body><p>Hello world</p></body></html>"
val baseUrl = "https://example.com/"

webView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl)

Java

String html = "<html><body><p>Hello world</p></body></html>";
String baseUrl = "https://example.com/";

mWebView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl);

Chọn các giá trị cho đối số một cách cẩn thận. Hãy cân nhắc thực hiện những bước sau:

  • baseUrl: đây là URL dùng để tải nội dung HTML của bạn. Đây phải là URL HTTP(S).
  • data: đây là nội dung HTML mà bạn muốn hiển thị dưới dạng một chuỗi.
  • mimeType: giá trị này thường phải được đặt thành text/html.
  • encoding: không được dùng khi baseUrl là URL HTTP(S), vì vậy được đặt thành null.
  • historyUrl: giá trị này được đặt thành cùng một giá trị với baseUrl.

Bạn nên sử dụng URL HTTP(S) làm baseUrl, vì điều này sẽ giúp ích đảm bảo ứng dụng của bạn tuân thủ chính sách về cùng nguồn gốc.

Nếu bạn không tìm được baseUrl phù hợp với nội dung của mình và muốn sử dụng loadData(), bạn phải mã hoá nội dung bằng mã hoá phần trăm hoặc Base64 mã hoá. Bạn nên chọn phương thức mã hoá Base64 và sử dụng API Android để mã hoá theo phương thức lập trình, như trong mã mẫu sau:

Kotlin

val encodedHtml: String = Base64.encodeToString(html.toByteArray(), Base64.NO_PADDING)

webView.loadData(encodedHtml, mimeType, "base64")

Java

String encodedHtml = Base64.encodeToString(html.getBytes(), Base64.NO_PADDING);

mWebView.loadData(encodedHtml, mimeType, "base64");

Những điều nên tránh

Có một số cách khác để tải nội dung trong ứng dụng, nhưng chúng tôi khuyên bạn chống lại chúng:

  • URL file:// và URL data: được coi là nguồn gốc không rõ ràng, nghĩa là chúng không thể tận dụng các API web mạnh mẽ như fetch() hoặc XMLHttpRequest. loadData() sử dụng data: URL trong nội bộ, vì vậy chúng tôi khuyến khích bạn sử dụng Hãy WebViewAssetLoader hoặc loadDataWithBaseURL().
  • Mặc dù WebSettings.setAllowFileAccessFromFileURLs()WebSettings.setAllowUniversalAccessFromFileURLs() có thể giải quyết vấn đề với file:// URL, bạn không nên đặt các lệnh này với true, vì làm như vậy khiến ứng dụng của bạn dễ bị tấn công khai thác. Bạn nên đặt rõ ràng các chế độ cài đặt này thành false ở mọi cấp độ API để có mức bảo mật mạnh nhất.
  • Cũng vì lý do đó, bạn không nên dùng file://android_assets/file://android_res/ URL. AssetsHandlerResourcesHandler là các lớp thay thế bất kỳ.
  • Tránh sử dụng MIXED_CONTENT_ALWAYS_ALLOW. Chế độ cài đặt này thường không cần thiết và làm giảm khả năng bảo mật của ứng dụng. Bạn nên tải nội dung trong ứng dụng qua cùng một giao thức — HTTP hoặc HTTPS—là tài nguyên của trang web của bạn và việc sử dụng MIXED_CONTENT_COMPATIBILITY_MODE hoặc MIXED_CONTENT_NEVER_ALLOW, khi phù hợp.