您可以提供網頁式內容 (例如 HTML、JavaScript 和 CSS),讓應用程式使用靜態編譯至應用程式,而非透過網際網路擷取。
應用程式內容不需存取網際網路,也不需消耗使用者的頻寬。如果內容是專為 WebView
設計,也就是需要與原生應用程式通訊,使用者就不會意外在網路瀏覽器中載入內容。
不過,應用程式內容有一些缺點。如要更新網路內容,您必須傳送新的應用程式更新,如果使用者的應用程式版本過舊,網站上的內容和裝置內容可能就會不相符。
WebViewAssetLoader
WebViewAssetLoader
可讓您有彈性且有效率的方式,在 WebView
物件中載入應用程式內的內容。這個類別支援下列項目:
- 使用 HTTP(S) 網址載入內容,以便與相同來源政策相容。
- 載入 JavaScript、CSS、圖片和 iframe 等子資源。
在主要活動檔案中加入 WebViewAssetLoader
。以下範例說明如何從資產資料夾載入簡易網頁內容:
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)); } }
您的應用程式必須根據自身需求設定 WebViewAssetLoader
執行個體。下一節將提供範例。
建立應用程式內素材資源和資源
WebViewAssetLoader
需要使用 PathHandler
執行個體,才能載入與指定資源路徑相對應的資源。雖然您可以實作這個介面,根據應用程式的需求擷取資源,但 Webkit 程式庫套件 AssetsPathHandler
和 ResourcesPathHandler
會分別載入 Android 資產和資源。
如要開始使用,請先為您的應用程式建立素材資源和資源。一般來說,您必須符合以下條件:
- HTML、JavaScript 和 CSS 等文字檔案都屬於素材資源。
- 圖片和其他二進位檔案屬於資源。
如要在專案中新增文字型網頁檔案,請按照下列步驟操作:
- 在 Android Studio 中,以滑鼠右鍵按一下「app」>「src」>「main」資料夾,然後依序選擇「New」>「Directory」。
- 將資料夾命名為「assets」。
- 在 assets 資料夾上按一下滑鼠右鍵,然後依序點選「新增」>「檔案」。
輸入
index.html
,然後按下「Return」或「Enter」鍵。 - 重複上述步驟,為
stylesheet.css
建立空白檔案。 - 在接下來兩個程式碼範例中,填入您建立的空白檔案。
```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;
}
```
如要在專案中新增圖片式網路檔案,請按照下列步驟操作:
將
Android_symbol_green_RGB.png
檔案下載至本機電腦。將檔案重新命名為
android_robot.png
。手動將檔案移至硬碟上專案的
main/res/drawable
目錄。
圖 4 顯示您新增的圖片,以及上述程式碼範例的文字,這些內容會在應用程式中算繪。
如要完成應用程式,請按照下列步驟操作:
註冊處理常式並設定
AssetLoader
,方法是將下列程式碼新增至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));
將下列程式碼新增至
onCreate()
方法,以載入內容:Kotlin
webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
Java
mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
混用應用程式內容和網站上的資源
您的應用程式可能需要載入來自網際網路的應用程式內內容和內容,例如採用網站 CSS 樣式的應用程式內 HTML 頁面。WebViewAssetLoader
支援此用途。如果所有已註冊的 PathHandler
執行個體都無法找到指定路徑的資源,WebView
就會改回從網際網路載入內容。如果您混合使用應用程式內內容和網站上的資源,請保留目錄路徑 (例如 /assets/
或 /resources/
) 供應用程式內資源使用。請避免將網站上的任何資源儲存在這些位置。
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);
如需應用程式內 HTML 頁面擷取網頁代管 JSON 資料的範例,請參閱 GitHub 中的 WebView
示範。
loadDataWithBaseURL
如果應用程式只需要載入 HTML 網頁,且不需要攔截子資源,請考慮使用不需要應用程式素材資源的 loadDataWithBaseURL()
。您可以按照下列程式碼範例使用:
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);
請謹慎選擇引數值,請把握以下幾項重點:
baseUrl
:這是載入 HTML 內容時做為依據的網址。這必須是 HTTP(S) 網址。data
:這是您要以字串形式顯示的 HTML 內容。mimeType
:這通常必須設為text/html
。encoding
:當baseUrl
是 HTTP(S) 網址時未使用,因此可設為null
。historyUrl
:已設為與baseUrl
相同的值。
我們強烈建議您使用 HTTP(S) 網址做為 baseUrl
,這有助於確保應用程式符合相同來源政策。
如果您找不到適合內容的 baseUrl
,且偏好使用 loadData()
,就必須使用百分比編碼或 Base64 編碼進行內容編碼。我們強烈建議您選擇 Base64 編碼,並使用 Android API 以程式輔助方式為此編碼,如以下程式碼範例所示:
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");
應避免的事項
您可以透過多種方式載入應用程式內的內容,但我們強烈建議您採用這些方法:
- 系統會將
file://
網址和data:
網址視為「不透明來源」,因此無法使用功能強大的網路 API,例如fetch()
或XMLHttpRequest
。loadData()
內部會使用data:
網址,因此建議改用WebViewAssetLoader
或loadDataWithBaseURL()
。 - 雖然
WebSettings.setAllowFileAccessFromFileURLs()
和WebSettings.setAllowUniversalAccessFromFileURLs()
可以解決file://
網址的問題,我們仍建議不要將這些項目設為true
,以免應用程式容易遭受檔案攻擊。為獲得最佳安全性,建議您在所有 API 級別中明確設為false
。 - 基於相同理由,我們建議您避免使用
file://android_assets/
和file://android_res/
網址。AssetsHandler
和ResourcesHandler
類別是用來取代替換作業。 - 請避免使用
MIXED_CONTENT_ALWAYS_ALLOW
。這項設定通常並非必要,因此會降低應用程式的安全性。建議您透過與網站資源相同的配置 (HTTP 或 HTTPS),載入應用程式內容,並視情況使用MIXED_CONTENT_COMPATIBILITY_MODE
或MIXED_CONTENT_NEVER_ALLOW
。