使用 WebView
将 Web 应用或网页作为客户端应用的一部分进行呈现。 WebView
类是 Android 的 View
类的扩展,可让您将网页显示为 activity 布局的一部分。它不包含功能完备的 Web 浏览器(例如导航控件或地址栏)的功能。默认情况下,WebView
的全部功能是显示网页。
WebView
可帮助您在应用中提供可能需要更新的信息,例如最终用户许可协议或用户指南。在您的 Android 应用中,您可以创建一个包含 WebView
的 Activity
,然后用它来显示托管在线的文档。
当您的应用向用户提供需要互联网连接才能检索的数据(例如电子邮件)时,WebView
也很有用。在这种情况下,您可能会发现,在 Android 应用中构建一个显示包含所有用户数据的网页的 WebView
比执行网络请求然后解析数据并在 Android 布局中呈现数据要容易。您可以设计一个针对 Android 设备定制的网页,然后在 Android 应用中实现一个加载该网页的 WebView
。
本文档介绍了如何开始使用 WebView
、如何将网页中的 JavaScript 绑定到 Android 应用中的客户端代码、如何处理页面导航以及如何在使用 WebView
时管理窗口。
在早期版本的 Android 上使用 WebView
为了在应用运行的设备上安全地使用较新的 WebView
功能,请添加 AndroidX 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
添加到您的应用,您可以在 activity 布局中包含 <WebView>
元素,或者在 onCreate()
中将整个 Activity
窗口设置为 WebView
。
在 activity 布局中添加 WebView
要在布局中向您的应用添加 WebView
,请将以下代码添加到 activity 的布局 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
或者,要在 activity 的 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 字符串加载 URL:
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 对话框)时,也会调用此类。要详细了解在此情境下的调试,请阅读调试 Web 应用。 - 使用
WebViewClient
处理影响内容呈现的事件,例如表单提交错误或导航。您还可以使用此子类来拦截 URL 加载。 - 通过修改
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
设计的 Web 应用,那么您可以使用 setUserAgentString()
定义自定义用户代理字符串,然后在您的网页中查询自定义用户代理,以验证请求您的网页的客户端是您的 Android 应用。
将 JavaScript 代码绑定到 Android 代码
开发专为您的 Android 应用中的 WebView
设计的 Web 应用时,您可以在 JavaScript 代码和客户端 Android 代码之间创建接口。例如,您的 JavaScript 代码可以调用 Android 代码中的方法来显示 Dialog
,而不是使用 JavaScript 的 alert()
函数。
要在您的 JavaScript 和 Android 代码之间绑定新接口,请调用 addJavascriptInterface()
,并向其传递要绑定到 JavaScript 的类实例以及 JavaScript 可调用以访问该类的接口名称。
例如,您可以在您的 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
的接口。此时,您的 Web 应用可以访问 WebAppInterface
类。例如,以下是一些 HTML 和 JavaScript 代码,当用户点击按钮时,这些代码会使用新接口创建 toast 消息:
<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 会启动处理 URL 的应用。通常情况下,默认 Web 浏览器会打开并加载目标 URL。但是,您可以针对您的 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()
方法,该方法会检查 URL 的主机是否与特定域匹配,如上例所示。如果匹配,则该方法返回 false,并且不会覆盖 URL 加载。它允许 WebView
照常加载 URL。如果 URL 的主机不匹配,则会创建一个 Intent
来启动用于处理 URL 的默认 Activity
,该 Activity 会解析为用户的默认 Web 浏览器。
处理自定义 URL
当请求使用自定义 URL 方案的资源和解析链接时,WebView
会应用限制。例如,如果您实现 shouldOverrideUrlLoading()
或 shouldInterceptRequest()
等回调,则 WebView
仅对有效 URL 调用它们。
例如,WebView
可能不会对以下链接调用您的 shouldOverrideUrlLoading()
方法:
<a href="showProfile">Show Profile</a>
无效的 URL(如上例所示)在 WebView
中的处理方式不一致,因此我们建议改用格式正确的 URL。您可以使用自定义方案或您的组织控制的域的 HTTPS URL。
您可以使用自定义方案,而不是像上一个示例那样在链接中使用简单字符串,例如:
<a href="example-app:showProfile">Show Profile</a>
然后您可以在 shouldOverrideUrlLoading()
方法中像这样处理此 URL:
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 主要用于为特定 URL 启动 Intent。实现时,请确保对 WebView
处理的 URL 返回 false
。但是,您不限于启动 Intent。您可以用前面代码示例中的任何自定义行为替换启动 Intent。
导航网页历史记录
当您的 WebView
覆盖 URL 加载时,它会自动累积访问过的网页的历史记录。您可以使用 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); }
如果您的应用使用 AndroidX 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()
不会执行任何操作。
处理设备配置更改
在运行时,当设备的配置发生更改时(例如用户旋转设备或关闭输入法编辑器 (IME) 时),activity 状态会发生变化。这些变化会导致 WebView
对象的 activity 被销毁并创建新的 activity,同时也会创建一个新的 WebView
对象,该对象会加载被销毁对象的 URL。要修改 activity 的默认行为,您可以在清单中更改它处理 orientation
更改的方式。要详细了解如何在运行时处理配置更改,请阅读处理配置更改。
管理窗口
默认情况下,打开新窗口的请求会被忽略。无论是通过 JavaScript 打开还是通过链接中的 target 属性打开,都是如此。您可以自定义 WebChromeClient
来提供您自己的多窗口打开行为。
为了使您的应用更安全,最好阻止弹窗和新窗口打开。实现此行为最安全的方法是将 "true"
传递给 setSupportMultipleWindows()
,但不要覆盖 setSupportMultipleWindows()
所依赖的 onCreateWindow()
方法。此逻辑会阻止任何在其链接中使用 target="_blank"
的页面加载。