Building web apps in WebView
如果要将 Web 应用程序(或仅仅是网页)作为 Android 客户端的一部分,则可以使用 WebView。WebView 类是 Android View 类的扩展,允许你将 Web 页面显示为 Activity 布局的一部分。但它不包含完整的 Web 浏览器的功能,例如导航控件或地址栏。默认情况下,WebView 所做的就是显示一个网页。
使用 WebView 的常见原因是,你希望在应用中提供需要更新的信息,例如最终用户协议或用户指南。在 Android 应用中,你可以创建包含 WebView 的 Activity,使用它来显示在线托管的文档。
使用 WebView 另一种常见情况是,应用向用户提供始终需要 Internet 连接来检索数据(如电子邮件)的数据。在这种情况下,你可能会发现构建 WebView 更容易,该应用显示包含所有用户数据的网页,而不是执行网络请求,然后解析数据并在 Android 布局中呈现数据。同时,你可以设计一个为 Android 设备量身定制的网页,然后在应用中实现加载网页的 WebView。
以下内容向你介绍如何使用 WebView 以及如何执行其他操作,例如处理页面导航以及将网页中的 JavaScript 绑定到 Android 应用中的客户端代码。
* 将 WebView 添加到你的应用程序
##一、将 WebView 添加到你的应用程序
要将 WebView 添加到应用程序,可以在 Activity 布局中包含
元素,或者在 onCreate()
方法中将整个 Activity 窗口设置为 WebView。
1.1 在 Activity 布局中添加 WebView
要在布局中向应用程序添加 WebView,请将以下代码添加到 Activity 的布局 XML 文件中:
要在 WebView 中加载网页,请使用 loadUrl()
。例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
1.2 在 onCreate() 中添加 WebView
要在 Activity 的 onCreate()
方法中向你的应用添加 WebView,请使用以下类似的逻辑:
WebView myWebView = new WebView(activityContext);
setContentView(myWebView);
然后加载页面:
myWebView.loadUrl("https://www.example.com");
或者从 HTML 字符串加载 URL:
// 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");
注意:HTML 可以执行的操作有一些限制。有关编码选项的详细信息,请参阅 loadData() 和 loadDataWithBaseURL()。
但是在此之前,应用必须能够访问 Internet。要访问 Internet,请在清单文件中请求 INTERNET 权限。例如:
...
这就是 WebView 显示网页的基本步骤。此外,你可以通过修改以下内容来自定义 WebView:
使用
WebChromeClient
启用全屏支持。当 WebView 需要获取更改宿主应用 UI 的权限时,也会调用此类,例如创建或关闭窗口以及向用户发送 JavaScript 对话框。要了解有关此调试的更多信息,请阅读调试 Web 应用程序。处理影响内容呈现的事件,例如表单提交错误或使用
WebViewClient
导航。你还可以使用此子类拦截 URL 加载。通过修改
WebSettings
启用 JavaScript。使用 JavaScript 访问已注入 WebView 的 Android framework 对象。
二、在 WebView 中使用 JavaScript
如果你需要在 WebView 加载的网页中使用 JavaScript,则必须为 WebView 启用 JavaScript。启用 JavaScript 后,你还可以在应用代码和 JavaScript 代码之间创建接口。
2.1 启用 JavaScript
WebView 默认禁用 JavaScript。你可以通过附加到 WebView 的 WebSettings 启用它。你可以使用 getSettings()
获取 WebSettings
,然后使用 setJavaScriptEnabled()
启用 JavaScript。
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
使用 WebSettings
可以设置你需要的各种其他设置。例如,如果你正在开发专门为 Android 应用中的 WebView 设计的 Web 应用程序,则可以使用 setUserAgentString()
定义自定义用户代理字符串,然后在网页中查询自定义用户代理以验证请求你的网页的客户实际上是你的 Android 应用。
2.2 将 JavaScript 代码绑定到 Android 代码
在开发专为 Android 应用中的 WebView 设计的 Web 应用程序时,你可以在 JavaScript 代码和 Android 客户端代码之间创建接口。例如,你的 JavaScript 代码可以调用 Android 代码中的方法来显示 Dialog,而不是使用 JavaScript 的 alert()
函数。
要绑定 JavaScript 和 Android 代码之间的新接口,请调用 addJavascriptInterface()
,向其传递一个类实例以绑定到你的 JavaScript 以及一个接口名称用来给 JavaScript 调用以访问该类实例。
例如,你可以在 Android 应用中创建以下类:
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();
}
}
警告:如果你已将 targetSdkVersion 设置为 17 或更高,则必须将
@JavascriptInterface
注解添加到你希望 JavaScript 可用的任何方法(该方法也必须是公共的)。如果你未提供注解,则在 Android 4.2 或更高版本上运行时,你的网页无法访问该方法。
在此示例中,WebAppInterface 类允许网页使用 showToast()
方法创建 Toast 消息。
你可以使用 addJavascriptInterface()
将此类绑定到 WebView 中运行的 JavaScript,并将接口命名为 Android。例如:
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
这将在 WebView 中创建一个名为 Android 的接口给 JavaScript。此时,你的 Web 应用程序可以访问 WebAppInterface
类。例如,以下是一些 HTML 和 JavaScript,当用户单击按钮时,使用新接口创建 Toast 消息:
无需从 JavaScript 初始化 Android 接口。WebView 会自动将其初始化并提供给你的网页。因此,单击该按钮,showAndroidToast()
函数使用 Android 接口调用 WebAppInterface.showToast()
方法。
注意:绑定到 JavaScript 的对象在另一个线程中运行,而不是在构造它的线程中运行。
警告:使用
addJavascriptInterface()
可以让 JavaScript 控制你的 Android 应用。这是一个非常有用的功能同时也是一个危险的安全问题。当 WebView 中的 HTML 不可信(例如,部分或全部 HTML 由未知的人或进程提供)时,攻击者可以写入执行客户端代码的 HTML,也可能是其他的任何代码。因此,除非你编写了 WebView 中显示的所有 HTML 和 JavaScript,否则不应使用addJavascriptInterface()
。你也不应允许用户在 WebView 中导航到不属于你自己的其他网页(而是允许用户的默认浏览器应用程序打开外部链接 - 默认情况下,用户的 Web 浏览器会打开所有 URL 链接,因此只有在按照以下所述部分处理页面导航时才要小心)。
三、处理页面导航
当用户从 WebView 中的网页单击链接时,Android 的默认行为是启动处理 URL 的应用。通常,默认 Web 浏览器会打开并加载目标 URL。但是,你可以为 WebView 覆写此行为,以便在 WebView 中打开链接。然后,你可以允许用户在 WebView 维护的网页历史记录中前后导航。
注意:出于安全原因,系统的浏览器不会与你的应用共享其数据。
要打开用户单击的链接,请使用 setWebViewClient()
为 WebView 提供 WebViewClient
。例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(MyWebViewClient);
现在,用户单击的链接都将在 WebView 中加载。
如果你想更好地控制链接的加载方式,请创建自己的 WebViewClient
来覆盖 shouldOverrideUrlLoading()
方法。例如:
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("https://www.example.com")) {
// This is my website, so do not override; let my WebView load the page
// 在 WebView 中加载该网页
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
// 使用其他应用加载
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
然后为 WebView 创建这个新 WebViewClient 的实例:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());
现在,当用户单击链接时,系统调用 shouldOverrideUrlLoading()
,它会检查 URL host 是否与特定域匹配(如上所述)。如果它匹配,则该方法返回 false 以便不覆写 URL 加载(它允许 WebView 加载 URL)。如果 URL host 不匹配,则会创建一个 Intent 来启动可处理 URL 的默认 Activity(一般是用户的默认 Web 浏览器)。
3.1 浏览网页历史记录
当 WebView 覆写 URL 加载时,它会自动记录访问过的网页历史。你可以使用 goBack()
和 goForward()
在历史记录中前后导航。
例如,以下是你的 Activity 如何使用设备后退按钮向后导航:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Check if the key event was the Back button and if there's history
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
// If it wasn't the Back key or there's no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
}
如果存在用户访问过网页历史记录,则 canGoBack()
方法返回 true
。同样,你可以使用 canGoForward()
来检查是否存在向前跳转的历史记录。如果你不执行此检查,那么一旦用户到达历史记录的末尾,goBack()
或 goForward()
就不会执行任何操作。
3.2 处理设备配置更改
在运行期间,当设备的配置发生更改(例如,用户旋转设备或关闭输入法编辑器(IME))时,会发生 Activity 状态更改。这些更改将导致销毁包含 WebView 对象的 Activity 并创建新 Activity,这也会创建一个新的 WebView 对象,该对象将加载被销毁对象的 URL。要修改 Activity 的默认行为,你可以在清单中更改其对配置变更的处理方式。要了解有关在运行时处理配置更改的更多信息,请阅读处理配置更改。
四、管理窗口
默认情况下,将忽略打开新窗口的请求。无论是通过 JavaScript 还是通过链接中的 target 属性打开它都是如此。你可以自定义 WebChromeClient
以提供打开多个窗口的行为。
警告:为了使你的应用更安全,最好防止弹出窗口和新窗口打开。实现此行为最安全的方法是将
“true”
传递给setSupportMultipleWindows()
,但不要覆盖setSupportMultipleWindows()
依赖的onCreateWindow()
方法。但请记住,此逻辑还是会阻止加载任何在其链接中使用target =“_ blank”
的页面。