使用 WebView
将 Web 应用或网页作为客户端应用的一部分提供。WebView
类是 Android View
类的扩展项,可让您将网页作为 activity 布局的一部分显示。但不包括完整开发的网络浏览器的功能,例如导航控件或地址栏。默认情况下,WebView
仅显示网页。
WebView
可��帮助您在应用中提供可能需要更新的信息,例如最终用户协议或用户指南。在 Android 应用中,您可以创建包含 WebView
的 Activity
,然后使用它来显示在线托管的文档。
当您的应用向用户提供需要互联网连接以检索数据(例如电子邮件)时,WebView
也可以提供帮助。在这种情况下,您可能会发现,与执行网络请求,然后解析数据并在 Android 布局中呈现数据相比,在 Android 应用中构建 WebView
以显示包含所有用户数据的网页更容易。您可以改为设计一个为 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 字符串加载网址:
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
需要权限以更改托管应用的界面(例如创建或关闭窗口,或向用户发送 JavaScript 对话框)时,也会调用此类。如需详细了解如何在这种情况下进行调试,请阅读调试 Web 应用。 - 使用
WebViewClient
处理影响内容渲染的事件,例如表单提交或导航错误。您还可以使用此子类拦截网址加载。 - 通过修改
WebSettings
启用 JavaScript。 - 使用 JavaScript 访问已注入到
WebView
中的 Android 框架对象。
在 WebView 中使用 JavaScript
如果您要在 WebView
中加载的网页使用 JavaScript,您必须为 WebView
启用 JavaScript。启用 JavaScript 后,您可以在应用代码和 JavaScript 代码之间创建接口。
启用 JavaScript
JavaScript 在 WebView
中默认处于停用状态。您可以通过附加到 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 代码,会在用户点按按钮时使用新界面创建消息框消息:
<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 ��要用于���动特定网址的 intent。实现该 API 时,请务必针对 WebView
句柄的网址返回 false
。但您并不仅限于启动 intent。您可以将启动 intent 替换为上述代码示例中的任何自定义行为。
浏览网页历史记录
当您的 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); }
如果您的应用使用 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
对象来加载已销毁对象的网址。如需修改 activity 的默认行为,您可以在清单中更改其处理 orientation
变更的方式。如需详细了解如何在运行时处理配置更改,请参阅���理配置更改。
管理窗口
默认情况下,系统会忽略打开新窗口的请求。无论是通过 JavaScript 还是通过链接中的 target 属性打开,都是如此。您可以自定义 WebChromeClient
,以提供您自己的打开多个窗口的行为。
为了提高应用的安全性,最好阻止打开弹出式窗口和新窗口。实现此行为的最安全方法是将 "true"
传递到 setSupportMultipleWindows()
中,但不替换 onCreateWindow()
方法(setSupportMultipleWindows()
所依赖的方法)。此逻辑会阻止加载在其链接中使用 target="_blank"
的任何页面。