使用 WebView
将 Web 应用或网页作为客户端应用的一部分提供。 WebView
类是 Android 的 View
类的扩展,它允许您将网页显示为活动布局的一部分。它不包含完全开发的 Web 浏览器(如导航控件或地址栏)的功能。默认情况下,所有 WebView
所做的只是显示一个网页。
WebView
可以帮助您在应用中提供可能需要更新的信息,例如最终用户协议或用户指南。在您的 Android 应用中,您可以创建一个包含 WebView
的 Activity
,然后使用它来显示您在线托管的文档。
WebView
也可以在您的应用向用户提供需要 Internet 连接才能检索数据(如电子邮件)的数据时提供帮助。在这种情况下,您可能会发现,在您的 Android 应用中构建一个显示包含所有用户数据的网页的 WebView
比执行网络请求、然后解析数据并在 Android 布局中呈现数据更容易。相反,您可以设计一个针对 Android 设备量身定制的网页,然后在您的 Android 应用中实现一个加载该网页的 WebView
。
本文档介绍了如何开始使用 WebView
、如何将 JavaScript 从您的网页绑定到 Android 应用中的客户端代码、如何处理页面导航以及如何在使用 WebView
时管理窗口。
在早期版本的 Android 上使用 WebView
为了在您的应用正在运行的设备上安全地使用更新的 WebView
功能,请添加 AndroidX Webkit 库。这是一个静态库,您可以将其添加到您的应用程序中以使用 android.webkit
API,这些 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 字符串加载 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。要获得 Internet 访问权限,请在清单文件中请求 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
类允许网页创建 Toast
消息,使用 showToast()
方法。
您可以使用 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 会启动一个处理 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
,这将解析为用户的默认 Web 浏览器。
处理自定义 URL
WebView
在请求使用自定义 URL 方案的资源和解析链接时会应用限制。例如,如果您实现了回调(如 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) 时),会发生活动状态更改。这些更改会导致 WebView
对象的活动被销毁并创建一个新的活动,该活动还会创建一个新的 WebView
对象,以加载已销毁对象的 URL。要修改活动的默认行为,您可以更改其在清单中处理 orientation
更改的方式。要详细了解如何在运行时处理配置更改,请阅读 处理配置更改。
管理窗口
默认情况下,打开新窗口的请求会被忽略。无论它们是由 JavaScript 打开还是由链接中的 target 属性打开,都是如此。您可以自定义 WebChromeClient
以提供您自己的打开多个窗口的行为。
为了提高应用程序的安全性,最好防止弹出窗口和新窗口打开。实现此行为最安全的方法是将 "true"
传递到 setSupportMultipleWindows()
中,但不要覆盖 onCreateWindow()
方法(setSupportMultipleWindows()
依赖于此方法)。此逻辑可防止任何在其链接中使用 target="_blank"
的页面加载。