在WebView中构建网络应用
如果你想在你的客户端应用上引入一个Web应用(仅仅是一个Web页),你就可以使用WebView。WebView是一个允许你在你的Activity布局中展示Web页面的View类的实现类。它不包括一个完全开发的浏览器的任何功能,如导航栏或地址栏。WebView所做的,仅仅是显示一个Web页面.
一个更加常见的使用WebView的场景是当你想要在你的应用中提供一些信息,而这些信息很可能需要更新时而使用WebView。就像用户协议和用户指南等。在你的应用中,你可以创建一个包含WebView的Activity,利用它来展示你的在线文档。
另一个WebView可能很有用的场景就是 你的应用需要对用户提供一些数据,而这些数据需要你经常性的访问网络来获得,就像email一样。在这种情况中,你会发现相比发送一个网络请求,然后解析其中的数据最后将他们展示在你的布局中来说,使用一个web 页来展示用户数据时十分简单的。进而,你可以专门为Android设备设计一个web页,然后通过WebView在你的Android应用中加载这个页面来实现。
这篇文章向你展示了如何启用一个WebView,如何做一些额外的事情,就像处理界面导航,从Web页绑定JavaScript到Android应用的客户端代码中去。
为你的应用程序增加一个WebView
为了在你的应用中增加WebView,简单的在布局文件中引入 <WebView>
标签就可以。例如,下面就是一个让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"
/>
通过loadUrl()
方法为WebView加载Web页。例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
在它开始工作之前,你的应用必须有访问网络的权限。为了获得这一权限,需要在你的manifest文件中请求INTERNET权限。例如:
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
这就是创建一个基本的展示Web页的WebView你所需要做的全部的事情。
在WebView中使用JavaScript
如果你想在你的WebView中加载带有JavaScript的Web页,你必须让你的WebView支持JavaScript。一旦JavaScript被启用了,你就可以在你的应用代码和JavaScript代码中创建接口了。
启用JavaScript
默认情况下,WebView中JavaScript是不启用的。你可以通过依附于WebView的WebSettings类实现,使用webView 的getSetting()
方法返回一个webSetting
对象,调用setJavaScriptEnabled()
来开启JavaScript。
例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebSettings提供了各种各样的你可能会发现很有用的其他设置。例如,如果你正在开发一个专门用WebView设计的Web应用,你通过setUserAgentString()
方法自定义了一个一个用户代理字符串。通过在你的Web页中查询这个自定义的用户代理字符串来核实客户端的请求是否来自的了Android应用。
将 JavaScript 代码绑定到 Android代码
当我们开发一个以WebView实现Web的Android应用时,你可以在你的javaScript代码和客户端Android之前创建接口。例如你的JavaScript代码可以调用一段你的Android代码来启动一个 Dialog,而不是通过在JavaScript中使用alert()
函数实现。
为了在你的Android代码和JavaScript代码间绑定一个接口,需要调用addJavascriptInterface()
方法,传递一个类实例到你绑定的JavaScript中,JavaScript可以调用以访问该类的接口名称。
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或者更高,你必须在你想要暴露给JavaScript的方法(方法必须是public方法)前加@JavascriptInterface
注解,如果你不提供这个注解,当在Android4.2或者更高的版本中运行该程序时,你的web 也将不能取得该方法。
在这个例子中,WebAppInterface
类允许web页通过使用showToast()
方法创建一个Toast信息。
你可以通过addJavascriptInterface()
方法绑定这个类到运行在你的WebView中JavaScript上,并且以“Android”命名接口。
例如:
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) {
Androi.showToast(toast);
}
</script>
这里不需要在JavaScript中初始化你的“Android”接口,WebView自动的让它在你的Web页中可获得。所以,在按钮的点击事件中,showAndroidToast()
方法使用“Android”接口来调用WebAppInterface.showToast()
方法。
注意:绑定到JavaScript上的对象将会运行在另一个线程中,而不是在构造它的线程中运行。
警告:使用addJavascriptInterface()
方法会允许JavaScript控制你的Android应用。这是一个很有用的特点,也是一个危险的安全问题。当WebView中的HTML代码不可信时(例如,HTML的一部分或全部是由未知的人或进程提供的)攻击者可以植入HTML代码执行你的客户端代码,这些代码可以是任意代码。正因为如此,除非你WebView中所有的HTML和JavaScript代码都是自己写的,否则就不要使用addJavascriptInterface()
方法。你也不应该让用户在你的WebView中导航到不属于你的Web站点上。(相反的是,允许用户使用默认浏览器应用来打开外部链接–在默认情况下,用户的浏览器可以打开所有链接,所以,在以下情况时要小心处理导航界面)
处理页面导航
当用户在你的WebView中点击了Web页中的一个链接,默认的行为是让Android 系统启动一个可以处理URLs的应用。通常情况下,默认的Web浏览器将会根据URL去加载。然而,你可以在你的WebView中重写这个行为,让链接在你的WebView中打开。你可以允许用户通过WebView中保存的浏览历史来通过导航栏进行前进或后退。
为了打开被用户点击的链接,简单的为WebView提供了WebViewClient类,通过 setWebViewClient()
方法获得,例如:
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,如果不匹配,就通过一个Intent,去启动默认程序来解析URLs。
浏览网页历史记录
当你的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代表这里有用户可以访问的Web页的历史记录。同样的,你可以使用 canGoForward()
方法来判断是否有可以前进的历史记录。如果你不做这个检查,一旦用户到达了历史记录的底端 goBack()
和 goForward()
方法将什么也不会做。