使用 WebView,將網頁應用程式或網頁做為用戶端應用程式的一部分。WebView 類別是 Android View 類別的擴充功能,可讓您在活動版面配置中顯示網頁。不包含完整網路瀏覽器的功能,例如導覽控制項或網址列。根據預設,WebView 只會顯示網頁。
WebView 可協助您在應用程式中提供可能需要更新的資訊,例如使用者協議或使用手冊。在 Android 應用程式中,您可以建立包含 WebView 的 Activity,然後用來顯示線上託管的文件。
如果應用程式向使用者提供的資料需要網際網路連線才能擷取 (例如電子郵件),WebView 也能派上用場。在這種情況下,您可能會發現,在 Android 應用程式中建構 WebView,顯示包含所有使用者資料的網頁,比執行網路要求、剖析資料並在 Android 版面配置中算繪資料更簡單。您可以改為設計專為 Android 裝置量身打造的網頁,然後在 Android 應用程式中實作 WebView,以便載入網頁。
本文說明如何開始使用 WebView、如何將網頁中的 JavaScript 繫結至 Android 應用程式中的用戶端程式碼、如何處理網頁瀏覽,以及如何在使用 WebView 時管理視窗。
在舊版 Android 上使用 WebView
如要在執行應用程式的裝置上安全使用較新的 WebView 功能,請新增 Jetpack Webkit 程式庫。這是一個靜態資料庫,您可以將其新增至應用程式,以便使用舊版平台不支援的 android.webkit API。
請按照下列方式,將金鑰新增至 build.gradle 檔案:
Kotlin
dependencies { implementation("androidx.webkit:webkit:1.8.0") }
Groovy
dependencies { implementation ("androidx.webkit:webkit:1.8.0") }
如要瞭解詳情,請前往 GitHub 查看 WebView 範例。
在應用程式中加入 WebView
如要在應用程式中加入 WebView,您可以在活動版面配置中加入 <WebView> 元素,或在 onCreate 中將整個 Activity 視窗設為 WebView。
在活動版面配置中新增 WebView
如要在版面配置中為應用程式新增 WebView,請在活動的版面配置 XML 檔案中加入下列程式碼:
<WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" />
如要在 WebView 中載入網頁,請使用 loadUrl,如以下範例所示:
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");
在 onCreate() 中新增 WebView
如要在活動的 onCreate() 方法中,將 WebView 新增至應用程式,請使用類似下方的邏輯:
Kotlin
val myWebView = WebView(activityContext) setContentView(myWebView)
Java
WebView myWebView = new WebView(activityContext); setContentView(myWebView);
然後載入頁面:
Kotlin
myWebView.loadUrl("http://www.example.com")
Java
myWebView.loadUrl("https://www.example.com");
或從 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");
應用程式必須連上網際網路。如要取得網路連線能力,請在資訊清單檔案中要求 INTERNET 權限,如以下範例所示:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
你可以透過下列任一方式自訂 WebView:
- 使用
WebChromeClient啟用全螢幕支援。當WebView需要權限來修改主機應用程式的 UI 時,也會呼叫這個類別,例如建立或關閉視窗,或向使用者傳送 JavaScript 對話方塊。如要進一步瞭解如何在這類情況下偵錯,請參閱「對網頁應用程式進行偵錯」。 - 處理影響內容算繪的事件,例如表單提交錯誤或使用
WebViewClient導覽。您也可以使用這個子類別攔截網址載入。 - 修改
WebSettings啟用 JavaScript。 - 使用 JavaScript 存取已插入
WebView的 Android 架構物件。
在 WebView 中使用 JavaScript
如果您要在 WebView 中載入的網頁使用 JavaScript,則必須為 WebView 啟用 JavaScript。啟用 JavaScript 後,您可以在應用程式程式碼和 JavaScript 程式碼之間建立介面。
啟用 JavaScript
WebView 預設會停用 JavaScript。您可以透過附加至 WebView 的 WebSettings 啟用這項功能。使用 getSettings 擷取 WebSettings,然後使用 setJavaScriptEnabled 啟用 JavaScript。
請參閱以下範例:
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 提供各種其他實用設定。舉例來說,如果您開發的網頁應用程式是專為 Android 應用程式中的 WebView 設計,則可以使用 setUserAgentString 定義自訂使用者代理程式字串,然後在網頁中查詢自訂使用者代理程式,確認要求網頁的用戶端是您的 Android 應用程式。
將 JavaScript 程式碼繫結至 Android 程式碼
開發專為 Android 應用程式中 WebView 設計的網頁應用程式時,您可以在 JavaScript 程式碼和用戶端 Android 程式碼之間建立介面。舉例來說,JavaScript 程式碼可以呼叫 Android 程式碼中的方法來顯示 Dialog,而不是使用 JavaScript 的 alert() 函式。
如要在 JavaScript 和 Android 程式碼之間繫結新介面,請呼叫 addJavascriptInterface,並傳遞要繫結至 JavaScript 的類別例項,以及 JavaScript 可呼叫來存取類別的介面名稱。
如要進一步瞭解 JavaScript 與原生程式碼之間的通訊,包括更現代化且安全的 API,請參閱「使用 JSBridge 存取原生 API」。
舉例來說,您可以在 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(); } }
在本例中,WebAppInterface 類別可讓網頁使用 showToast() 方法建立 Toast 訊息。
您可以使用 addJavascriptInterface(),將這個類別繫結至 WebView 中執行的 JavaScript,如下列範例所示:
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");
這會為在 WebView 中執行的 JavaScript 建立名為 Android 的介面。此時,網頁應用程式可以存取 WebAppInterface 類別。舉例來說,以下 HTML 和 JavaScript 會在使用者輕觸按鈕時,使用新介面建立浮動式訊息:
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" /> <script type="text/javascript"> function showAndroidToast(toast) { Android.showToast(toast); } </script>
您不需要從 JavaScript 初始化 Android 介面。WebView 會自動將其提供給網頁。因此,當使用者輕觸按鈕時,showAndroidToast() 函式會使用 Android 介面呼叫 WebAppInterface.showToast() 方法。
處理網頁瀏覽
使用者在 WebView 中輕觸網頁上的連結時,Android 預設會啟動處理網址的應用程式。通常,系統會開啟預設的網路瀏覽器,並載入到達網頁網址。不過,您可以覆寫 WebView 的這項行為,讓連結在 WebView 中開啟。接著,使用者就能透過 WebView 返回上一頁或前往下一頁,瀏覽網頁歷史記錄。
如要開啟使用者輕觸的連結,請使用 setWebViewClient 為 WebView 提供 WebViewClient。使用者輕觸的所有連結都會在 WebView 中載入。如要進一步控制點選連結的載入位置,請建立自己的 WebViewClient,覆寫 shouldOverrideUrlLoading 方法。以下範例假設 MyWebViewClient 是 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; } }
然後為 WebView 建立這個新 WebViewClient 的例項:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.webViewClient = MyWebViewClient()
Java
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.setWebViewClient(new MyWebViewClient());
現在,當使用者輕觸連結時,系統會呼叫 shouldOverrideUrlLoading 方法,檢查網址主機是否與特定網域相符,如前例所示。如果相符,方法會傳回 false,且不會覆寫網址載入。讓 WebView 照常載入網址。如果網址主機不相符,系統會建立 Intent,啟動預設的 Activity 處理網址,並解析為使用者的預設網路瀏覽器。
處理自訂網址
WebView 會在要求資源和解析使用自訂網址配置的連結時,套用限制。舉例來說,如果您實作 shouldOverrideUrlLoading 或 shouldInterceptRequest 等回呼,WebView 只會針對有效網址叫用這些回呼。
舉例來說,WebView 可能不會針對以下連結呼叫 shouldOverrideUrlLoading 方法:
<a href="showProfile">Show Profile</a>
WebView 處理無效網址 (如上例所示) 的方式不一致,因此建議使用格式正確的網址。您可以使用自訂配置,或貴機構控管網域的 HTTPS 網址。
您可以使用自訂配置,而非像前一個範例一樣在連結中使用簡單字串,例如:
<a href="example-app:showProfile">Show Profile</a>
接著,您可以在 shouldOverrideUrlLoading 方法中處理這個網址,如下所示:
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; }
shouldOverrideUrlLoading API 主要用於啟動特定網址的意圖。實作時,請務必針對 WebView 處理的網址傳回 false。不過,您不只能啟動意圖。您可以在上述程式碼範例中,將啟動意圖替換為任何自訂行為。
瀏覽網頁歷史記錄
當 WebView 覆寫網址載入作業時,系統會自動累積所造訪網頁的記錄。你可以使用 goBack 和 goForward 在歷史記錄中前後瀏覽。
舉例來說,以下說明 Activity 如何使用裝置的返回按鈕來返回上一頁:
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); }
如果應用程式使用 Jetpack AppCompat 1.6.0 以上版本,您可以進一步簡化上述程式碼片段:
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(); } }
如果使用者有可造訪的網頁歷史記錄,canGoBack 方法會傳回 true。同樣地,您可以使用 canGoForward 檢查歷史記錄是否有下一頁可以瀏覽。如果您未執行這項檢查,則在使用者到達歷史記錄結尾後,goBack 和 goForward 不會執行任何動作。
如要在活動重新建立或背景程序回收時保留瀏覽記錄,請使用 saveState 將 WebView 的瀏覽記錄序列化為 Bundle,並使用 restoreState 還原。如需安全管理已儲存狀態大小限制,以及避免 TransactionTooLargeException 的指南,請參閱「有效管理 WebView 狀態」。
處理裝置設定變更
在執行階段,當裝置設定變更時,活動狀態也會隨之變更,例如使用者旋轉裝置或關閉輸入法編輯器 (IME)。這些變更會導致 WebView 物件的活動遭到刪除,並建立新的活動,這也會建立新的 WebView 物件,載入已刪除物件的網址。您可以在 onSaveInstanceState() 內使用 saveState,並在重新建立時使用 restoreState 還原使用者的導覽結構定義。如要瞭解如何安全管理狀態大小,請參閱「有效管理 WebView 狀態」。
如要修改活動的預設行為,您可以在資訊清單中變更活動處理 orientation 變更的方式。如要進一步瞭解如何在執行階段處理設定變更,請參閱「處理設定變更」。
管理視窗
根據預設,系統會忽略開啟新視窗的要求。無論是由 JavaScript 開啟,還是由連結中的 target 屬性開啟,都是如此。您可以自訂 WebChromeClient,提供開啟多個視窗的專屬行為。
為提高應用程式安全性,建議您禁止開啟彈出式視窗和新視窗。如要實作這項行為,最安全的做法是將 "true" 傳遞至 setSupportMultipleWindows,但不要覆寫 setSupportMultipleWindows() 依賴的 onCreateWindow 方法。這項邏輯會防止任何在連結中使用 target="_blank" 的網頁載入。
管理記憶體和效能
WebView 採用 Chromium 引擎,具有獨立程序和原生程式碼,因此基準記憶體用量高於標準 Android 檢視畫面。重複使用 WebView 執行個體並妥善刪除,是維持應用程式穩定的關鍵。
如要進一步瞭解如何診斷及最佳化應用程式在使用網路內容時的記憶體用量,請參閱「管理及診斷 WebView 記憶體」。