如果要將Web應(yīng)用程序(或僅僅是網(wǎng)頁)作為客戶端應(yīng)用程序的一部分提供,則可以使用WebView執(zhí)行此操作。 WebView類是Android的View類的擴(kuò)展,允許您將Web頁面顯示為活動布局的一部分。它不包括完全開發(fā)的Web瀏覽器的任何功能,例如導(dǎo)航控件或地址欄。默認(rèn)情況下,WebView所做的就是顯示一個網(wǎng)頁。
本文檔向您展示了如何開始使用WebView以及如何執(zhí)行其他操作,例如處理頁面導(dǎo)航以及將網(wǎng)頁中的JavaScript綁定到Android應(yīng)用程序中的客戶端代碼。
一、將WebView添加到您的應(yīng)用程序
要將WebView添加到應(yīng)用程序,可以在活動布局中包含<WebView>元素,也可以將整個“活動”窗口設(shè)置為onCreate()中的WebView。
在活動布局中添加WebView
將以下代碼添加到活動的布局XML文件中:
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
要在WebView中加載網(wǎng)頁,請使用loadUrl()。 例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
在onCreate()中添加WebView
在活動的onCreate()方法中向您的應(yīng)用添加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可以執(zhí)行的操作有一些限制。 有關(guān)編碼選項的詳細(xì)信息,請參閱loadData()和loadDataWithBaseURL()。
權(quán)限
在此之前,您的應(yīng)用必須能夠訪問Internet。 請在清單文件中請求INTERNET權(quán)限:
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
這就是顯示網(wǎng)頁的基本WebView所需的全部內(nèi)容。 此外,您可以通過修改以下內(nèi)容來自定義WebView:
- 使用
WebChromeClient啟用全屏支持。 當(dāng)WebView需要更改主機(jī)應(yīng)用程序UI的權(quán)限時,也會調(diào)用此類,例如創(chuàng)建或關(guān)閉窗口以及向用戶發(fā)送JavaScript對話框。 - 處理影響內(nèi)容呈現(xiàn)的事件,例如表單提交上的錯誤或使用
WebViewClient導(dǎo)航。 您還可以使用此子類來攔截URL加載。 - 通過修改
WebSettings啟用JavaScript。 - 使用
JavaScript訪問已注入WebView的Android框架對象。
二、在WebView中使用JavaScript
如果您計劃在WebView中加載的網(wǎng)頁使用JavaScript,則必須為WebView啟用JavaScript。 啟用JavaScript后,您還可以在應(yīng)用代碼和JavaScript代碼之間創(chuàng)建接口。
啟用JavaScript
默認(rèn)情況下,在WebView中禁用JavaScript。
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebSettings提供對您可能會發(fā)現(xiàn)有用的各種其他設(shè)置的訪問。 例如,如果您正在開發(fā)專門為Android應(yīng)用程序中的WebView設(shè)計的Web應(yīng)用程序,則可以使用setUserAgentString()定義自定義用戶代理字符串,然后在網(wǎng)頁中查詢自定義用戶代理以驗證,請求您的網(wǎng)頁的客戶實際上是您的Android應(yīng)用。
將JavaScript代碼綁定到Android代碼
在開發(fā)專為Web應(yīng)用程序中的WebView設(shè)計的Web應(yīng)用程序時,您可以在JavaScript代碼和客戶端Android代碼之間創(chuàng)建接口。 例如,您的JavaScript代碼可以調(diào)用Android代碼中的方法來顯示Dialog,而不是使用JavaScript的alert()函數(shù)。
要綁定JavaScript和Android代碼之間的新接口,請調(diào)用addJavascriptInterface(),向其傳遞一個類實例以綁定到您的JavaScript以及您可以調(diào)用以訪問該類的接口名稱。
例如,您可以在Android應(yīng)用中包含以下類:
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設(shè)置為17或更高,則必須將@JavascriptInterface注釋添加到您希望JavaScript可用的任何方法(該方法也必須是公共的)。 如果您未提供注釋,則在Android 4.2或更高版本上運(yùn)行時,您的網(wǎng)頁無法訪問該方法。
在此示例中,WebAppInterface類允許網(wǎng)頁使用showToast()方法創(chuàng)建Toast消息。
您可以使用addJavascriptInterface()將此類綁定到在WebView中運(yùn)行的JavaScript,并將接口命名為Android。 例如:
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
這將在WebView中創(chuàng)建一個名為Android for JavaScript的界面。 此時,您的Web應(yīng)用程序可以訪問WebAppInterface類。 例如,以下是一些HTML和JavaScript,當(dāng)用戶單擊按鈕時,使用新界面創(chuàng)建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會自動將其提供給您的網(wǎng)頁。 因此,只需單擊按鈕,showAndroidToast()函數(shù)就會使用Android界面調(diào)用WebAppInterface.showToast()方法。
注意:綁定到
JavaScript的對象在另一個線程中運(yùn)行,而不是在構(gòu)造它的線程中運(yùn)行。
警告:使用
addJavascriptInterface()可以讓JavaScript控制您的Android應(yīng)用。 這可能是一個非常有用的功能或危險的安全問題。 當(dāng)WebView中的HTML不可信(例如,部分或全部HTML由未知的人或進(jìn)程提供)時,攻擊者可以包含執(zhí)行客戶端代碼的HTML,也可能包括攻擊者選擇的任何代碼。 因此,除非您編寫了WebView中顯示的所有HTML和JavaScript,否則不應(yīng)使用addJavascriptInterface()。 您也不應(yīng)允許用戶在WebView中導(dǎo)航到不屬于您自己的其他網(wǎng)頁(而是允許用戶的默認(rèn)瀏覽器應(yīng)用程序打開外部鏈接 - 默認(rèn)情況下,用戶的Web瀏覽器會打開所有URL鏈接,因此 只有在按照以下部分所述處理頁面導(dǎo)航時才要小心)。
三、處理頁面導(dǎo)航
當(dāng)用戶從WebView中的網(wǎng)頁單擊鏈接時,Android的默認(rèn)行為是啟動處理URL的應(yīng)用程序。 通常,默認(rèn)Web瀏覽器會打開并加載目標(biāo)URL。 但是,您可以為WebView覆蓋此行為,以便在WebView中打開鏈接。 然后,您可以允許用戶在WebView維護(hù)的網(wǎng)頁歷史記錄中前后導(dǎo)航。
注意:出于安全考慮,系統(tǒng)的瀏覽器應(yīng)用程序不會與您的應(yīng)用共享其應(yīng)用程序數(shù)據(jù)。
要打開用戶單擊的鏈接,請使用setWebViewClient()為WebView提供WebViewClient。 例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(MyWebViewClient);
現(xiàn)在,用戶在WebView中單擊加載的所有鏈接。
如果您想要更好地控制單擊鏈接的加載位置,請創(chuàng)建自己的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
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創(chuàng)建這個新WebViewClient的實例:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());
現(xiàn)在,當(dāng)用戶單擊鏈接時,系統(tǒng)調(diào)用shouldOverrideUrlLoading(),它會檢查URL主機(jī)是否與特定域匹配(如上所述)。 如果它匹配,則該方法返回false以便不覆蓋URL加載(它允許WebView像往常一樣加載URL)。 如果URL主機(jī)不匹配,則會創(chuàng)建一個Intent以啟動處理URL的默認(rèn)活動(解析為用戶的默認(rèn)Web瀏覽器)。
瀏覽網(wǎng)頁歷史記錄
當(dāng)WebView覆蓋URL加載時,它會自動累積訪問過的網(wǎng)頁的歷史記錄。 您可以使用goBack()和goForward()在歷史記錄中前后導(dǎo)航。
例如,以下是您的Activity如何使用設(shè)備后退按鈕向后導(dǎo)航:
@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);
}
}
如果實際存在用戶要訪問的網(wǎng)頁歷史記錄,則canGoBack()方法返回true。 同樣,您可以使用canGoForward()來檢查是否存在轉(zhuǎn)發(fā)歷史記錄。 如果您不執(zhí)行此檢查,那么一旦用戶到達(dá)歷史記錄的末尾,goBack()或goForward()就不會執(zhí)行任何操作。
處理設(shè)備配置更改
在運(yùn)行時期間,當(dāng)設(shè)備的配置發(fā)生更改(例如,用戶旋轉(zhuǎn)設(shè)備或關(guān)閉輸入法編輯器(IME)時)會發(fā)生活動狀態(tài)更改。 這些更改將導(dǎo)致銷毀WebView對象的活動并創(chuàng)建新活動,這也會創(chuàng)建一個新的WebView對象,該對象將加載銷毀對象的URL。 要修改活動的默認(rèn)行為,您可以更改其在清單中處理方向更改的方式。
四、管理窗口
默認(rèn)情況下,將忽略打開新窗口的請求。 無論是通過JavaScript還是通過鏈接中的target屬性打開它都是如此。 您可以自定義WebChromeClient以提供打開多個窗口的行為。
警告:為了使您的應(yīng)用更安全,最好防止彈出窗口和新窗口打開。 實現(xiàn)此行為的最安全方法是將“
true”傳遞給setSupportMultipleWindows(),但不覆蓋setSupportMultipleWindows()依賴的onCreateWindow()方法。 但請記住,此邏輯還會阻止任何在其鏈接中使用target =“_ blank”的頁面加載。