本篇文章翻译自Building Web Apps in WebView
如果你想分发一个web应用(或者只是一个web页面),其只是客户端app的一部分,你可以使用WebView
来做到。WebView
类是Android的View
类的扩展,它允许你作为Activity布局的一部分来显示web页面。它不包括完全开发的web浏览器的任意特性,比如导航控制或者地址栏。WebView
所做的,默认情况下,是显示一个web页面。
使用WebView
的一个常见的有用的场景是,当你在你的app中想提供一些信息,而它们有可能需要更新,比如一个最终用户协议或者一个用户使用指南。在Android app中,你可以创建一个包含WebView
的Activity
,然后使用它来显示你的在线文档。
WebView
可以带来帮助的另一个场景就是,如果你的app给用户提供的数据总是需要网络连接以获取数据,比如email。在这种情况下,你可能会发现在Android app中创建WebView
来显示带有所有用户数据的web页面,要比发送网络请求然后解析数据并且在一个Android布局中渲染出来要简单得多。作为替代,你可以设计一个针对Android设备量身定做的web页面,并且在Android app中实现WebView
来加载此页面。
这篇文档示例你如何开始使用WebView
并且如何做一些附加的事情,比如在你的Android app中处理页面导航,将web页面中的JavaScript和客户端代码绑定起来。
在你的app中添加一个WebView
要在你的app中添加一个WebView
,只需要简单地将WebView
元素添加到你的activity的布局中即可。比如,这里是一个WebView
填充屏幕的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
要在WebView
中加载一个web页面,调用loadUrl
。比如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
在上述代码可以工作之前,你的app需要可以访问网络。为了可以访问网络,在配置文件中声明INTERNET
权限。比如:
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
对于一个显示web页面的基本WebView
,这就是你所需要做的。
在WebView中使用JavaScript
如果你准备加载到WebView
的web页面使用JavaScript,你必须为WebView
开启JavaScript。当JavaScript启用后,你还可以在你的app代码与JavaScript代码之间创建借口。
启用JavaScript
WebView
默认情况下是禁用JavaScript的。你可以通过附加在WebView
上的WebSettings
开启JavaScript。你可以使用getSettings()
获取WebSettings
,然后通过setJavaScriptEnabled()
来启用JavaScript。
比如:
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebSettings
提供了访问许多其它你可能觉得有用的设置的接口。比如,如果你在Android app开发了一个专为WebView
设计的web app,你可以使用setUserAgentString()
来定义一个自定义的用户代理,然后在你的web页面中查询自定义的用户代理来验证请求你的web页面的客户端实际上是你的Android app。
将JavaScript代码绑定到Android代码
当你在Android app里开发了一个专为WebView
设计的web app时,你可以在JavaScript代码与客户端Android代码之间常见接口。比如,JavaScript代码可以调用Android代码中的一个方法来显示一个Dialog
,而不是使用JavaScript的alert()
方法。
要在JavaScript与Android代码之间绑定一个新的接口,可以调用addJavaScriptInterface()
方法,传入一个类的实例来绑定到JavaScript和一个接口名,JavaScript通过它可以访问类。
比如,你可以将下面的类包含到你的Android app中:
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的方法上(该方法同时必须是public的)。如果你没有提供该注解,当在Android 4.2或更高版本运行时,web页面则不能访问该方法。
在这个例子中,WebAppInterface
类允许web页面使用showToast()
方法创建一条Toast
消息。
你可以使用addJavascriptInterface()
方法并且将接口命名为Android
,将该类绑定到在WebView
中运行的JavaScript上。比如:
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
上面代码为运行在WebView
中的JavaScript创建了一个名为Android
的接口。在这个时候,你的web app就可以访问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
可以自动地使它对于你的web页面可用。所以,在点击按钮时,showAndroidToast()
方法使用了Android
接口来调用WebAppInterface.showToast()
方法。
注意:绑定到JavaScript上的对象运行在另一个线程且不在创建它的那个线程。
警告:使用
addJavascriptInterface()
方法允许JavaScript控制你的Android app。这可以是一个很有用的特性或者是一个危险地安全问题。当WebView
中的HTML不可信任时(比如,部分或者全部的HTML是由未知的人或者进程所提供),攻击者可以包含可以运行客户端代码的HTML代码,并且可能是攻击者选中的任意代码。正因如此,除非显示在WebView
中的HTML和JavaScript全部都是你写的,否则不要使用addJavascriptInterface()
。同样地,在你的WebView
里,你也不要让用户可以导航到一个不是你自己的web页面(作为替代,默认情况下你可以允许用户的默认浏览器app打开外部链接,即使用用户的web浏览器打开所有URL链接,所以如果你处理页面导航时要小心,正如下面所描述的那样)。
处理页面导航
当用户在WebView
的web页面里点击一个链接时,对于Android来说默认的行为是启动一个app来处理URL。通常,默认的web浏览器打开并且加载目标URL。但是,你可以为你的WebView
覆盖这种行为,所以在WebView
里可以代开这些链接。你可以允许用户在他们的web页面历史中前后导航,这由WebView
来维护。
当用户点击后要打开链接,简单地使用setWebViewClient()
方法为WebView
提供一个WebViewClient
即可。比如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient());
就是这样。现在用户点击的所有链接都会被WebView
来加载。
如果你想对点击链接加载进行更多的控制,可以创建你自己的WebViewClient
并且复写shouldOverrideUrlLoading()
方法。比如:
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.example.com")) {
// This is my web site, so do not override; let my WebView load the page
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的主机是否符合指定的域名(如上面所定义的)。如果符合,这个方法会返回false,来实现不覆盖加载URL的逻辑(它允许WebView
像往常一样加载URL)。如果URL的主机不符合,那么会创建一个Intent
用于启动默认的Activity来处理URL(它会解析为用户的默认web浏览器)。
Web页面历史导航
当你的WebView
覆盖URL加载时,它会自动将浏览过的web页面的历史累加起来。你可以使用goBack()
和goForward()
在历史中向前和向后导航。
比如,这里是你的Activity
使用设备的Back按钮向后导航:
@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);
}
如何有web页面历史可供用户浏览,canGoBack()
方法会返回true
。同样的,你可以使用canGoForward()
方法来检查是否有向前的历史。如果你不执行这项检查,那么一旦用户到达历史的尽头,goBack()
和goForward()
将什么都不做。
回到专题 Web应用