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) cho ứng dụng của mình bằng cách biên dịch tĩnh vào ứng dụng thay vì tìm nạp qua Internet.
Nội dung trong ứng dụng không yêu cầu kết nối Internet hoặc tiêu thụ băng thông của người dùng. Nếu nội dung được thiết kế riêng cho WebView
(nghĩa là nội dung phụ thuộc vào việc giao tiếp với ứng dụng gốc), thì người dùng không thể vô tình tải nội dung đó trong 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, bạn cần phải chuyển một bản cập nhật ứng dụng mới. Do đó, nếu người dùng sử dụng phiên bản ứng dụng lỗi thời, thì nội dung trên trang web có thể không khớp với nội dung trong ứng dụng.
WebViewAssetLoader
WebViewAssetLoader
là một cách linh hoạt và hiệu quả để tải nội dung trong ứng dụng trong đối tượng WebView
. Lớp này hỗ trợ các mục sau:
- Tải nội dung bằng một URL HTTP(S) để đảm bảo khả năng tương thích với chính sách cùng nguồn gốc.
- Đang 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. Sau đây là 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 của bạn phải định cấu hình một thực thể WebViewAssetLoader
cho phù hợp với nhu cầu của ứng dụng. Phần tiếp theo có một ví dụ.
Tạo thành phần và tài nguyên trong ứng dụng
WebViewAssetLoader
dựa vào các thực thể PathHandler
để tải các 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 nhu cầu của ứng dụng, nhưng thư viện Webkit sẽ gói AssetsPathHandler
và ResourcesPathHandler
tương ứng để tải thành phần và tài nguyên Android.
Để 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 nằm trong thành phần.
- Hình ảnh và các tệp nhị phân khác thuộc về tài nguyên.
Để thêm các tệp web dựa trên văn bản vào một dự án, hãy làm như sau:
- Trong Android Studio, hãy nhấp chuột phải vào thư mục app > src > main (ứng dụng > src > chính) rồi chọn New > Directory (Mới > Thư mục).
- Đặt tên thư mục là "Assets".
- Nhấp chuột phải vào thư mục Assets (tài sản), rồi nhấp vào New > File (Mới > Tệp).
Nhập
index.html
rồi nhấn phím Return hoặc Enter. - Lặp lại bước trước đó để tạo một tệp trống cho
stylesheet.css
. - Điền vào các tệp trống mà bạn đã tạo bằng nội dung trong 2 mã mẫu tiếp theo.
```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:
Tải tệp
Android_symbol_green_RGB.png
xuống máy cục bộ.Đổi tên tệp thành
android_robot.png
.Di chuyển tệp theo cách thủ công vào thư mục
main/res/drawable
của dự án trên ổ đĩa cứng.
Hình 4 cho thấy hình ảnh bạn đã thêm và văn bản của các mã mẫu trước đó hiển thị trong ứng dụng.
Để hoàn tất ứng dụng, hãy làm như sau:
Đăng ký các trình xử lý và định cấu hình
AssetLoader
bằng cách thêm mã sau vào phương thứconCreate()
: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));
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 được tạo kiểu theo CSS của trang web.
WebViewAssetLoader
hỗ trợ trường hợp sử dụng này. Nếu không thực thể PathHandler
nào đã đăng ký có thể tìm thấy tài nguyên cho đường dẫn đã cho, thì WebView
sẽ quay lại sử dụng phương thức 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 trên trang web của mình, hãy đặt trước đường dẫn thư mục (chẳng hạn như /assets/
hoặc /resources/
) cho tài nguyên trong ứng dụng. Tránh lưu trữ bất kỳ tài nguyên nào từ trang web của bạn ở những 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 để biết ví dụ về trang HTML trong ứng dụng tìm nạp dữ liệu JSON được lưu trữ trên web.
tảiDataWithBaseURL
Khi ứng dụng của bạn chỉ cần tải một trang HTML mà không cần chặn các tài nguyên phụ, hãy cân nhắc sử dụng loadDataWithBaseURL()
mà không yêu cầu thành phần ứng dụng. Bạn có thể sử dụng mã này như trong mã mẫu sau:
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 giá trị đố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 để tải nội dung HTML của bạn. Đây phải là một URL HTTP(S).data
: đây là nội dung HTML mà bạn muốn hiển thị dưới dạng chuỗi.mimeType
: giá trị này thường phải được đặt thànhtext/html
.encoding
: thuộc tính này không được dùng khibaseUrl
là một URL HTTP(S) nên có thể đặt thànhnull
.historyUrl
: thuộc tính này được đặt thành cùng giá trị vớibaseUrl
.
Bạn nên dùng URL loại HTTP(S) làm baseUrl
, vì điều này giúp đảm bảo ứng dụng của bạn tuân thủ chính sách cùng nguồn gốc.
Nếu không tìm thấy 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 mã hoá Base64.
Bạn nên chọn phương thức mã hoá Base64 và sử dụng các API Android để mã hoá phương thức này 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
Ngoài ra, còn có một số cách khác để tải nội dung trong ứng dụng, nhưng bạn nên làm như vậy:
- Các URL
file://
và URLdata:
được xem là nguồn gốc không rõ ràng, tức là chúng không thể tận dụng các API web mạnh mẽ nhưfetch()
hoặcXMLHttpRequest
.loadData()
sử dụng nội bộ URLdata:
. Vì vậy, bạn nên sử dụngWebViewAssetLoader
hoặcloadDataWithBaseURL()
. - Mặc dù
WebSettings.setAllowFileAccessFromFileURLs()
vàWebSettings.setAllowUniversalAccessFromFileURLs()
có thể giải quyết các vấn đề với URLfile://
, nhưng bạn không nên đặt các URL này thànhtrue
vì làm như vậy sẽ khiến ứng dụng của bạn dễ bị khai thác dựa trên tệp. Bạn nên đặt rõ ràng các giá trị này thànhfalse
ở mọi cấp độ API để có mức bảo mật mạnh nhất. - Với lý do tương tự, bạn không nên dùng các URL
file://android_assets/
vàfile://android_res/
. Các lớpAssetsHandler
vàResourcesHandler
là các lớp thay thế không thể dùng. - 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àm tài nguyên của trang web và sử dụngMIXED_CONTENT_COMPATIBILITY_MODE
hoặcMIXED_CONTENT_NEVER_ALLOW
(nếu phù hợp).