Web Apps
實(shí)現(xiàn)一個(gè)Android應(yīng)用的兩個(gè)基本方式是:客戶端應(yīng)用和Web應(yīng)用。前者使用Android SDK開發(fā)并且以一個(gè)APK方式安裝在用戶的設(shè)備上短蜕,后者使用標(biāo)準(zhǔn)的Web開發(fā)蓄诽,不需要在用戶設(shè)備上安裝就漾,通過一個(gè)Web瀏覽器獲取粱坤。
如果我們?yōu)锳ndroid設(shè)備提供一個(gè)基于web的app店印,我們可以確定的是主流的Android瀏覽器(WebView框架)允許我們指定視窗和樣式屬性冈在,確保我們的網(wǎng)頁在所有屏幕配置上都能夠呈現(xiàn)出合適的尺寸和大小。
我們不應(yīng)該開發(fā)一個(gè)app僅僅用來展示我們的網(wǎng)站按摘。相反的包券,網(wǎng)頁應(yīng)該嵌入在我們的app中。我們甚至可以在Android應(yīng)用和網(wǎng)頁之間定義一個(gè)接口院峡,以此允許我們網(wǎng)頁中的JavaScript可以調(diào)用Android應(yīng)用的APIs兴使。
使Web Apps支持不同的屏幕
因?yàn)楦鞣NAndroid設(shè)備有不同的屏幕大小及像素密度,我們在web設(shè)計(jì)的時(shí)候應(yīng)該考慮這些因素照激,以便我們的網(wǎng)頁總能以合適的大小展現(xiàn)出來发魄。
當(dāng)網(wǎng)頁的目標(biāo)設(shè)備是Android設(shè)備的時(shí)候,有兩個(gè)方面的因素需要我們考慮:
Viewport:Viewpost是一個(gè)矩形區(qū)域,它為我們的網(wǎng)頁提供一個(gè)可繪制區(qū)域励幼。我們可以指定不同的Viewpost屬性汰寓,例如大小和初始縮放百分比。最重要的是Viewport的寬度苹粟,它定義了這個(gè)網(wǎng)頁視圖的橫向可用像素?cái)?shù)(可用CSS像素的數(shù)量)有滑。
屏幕密度:Web類和Android大部分的網(wǎng)頁瀏覽器將CSS像素值轉(zhuǎn)換為獨(dú)立設(shè)備像素值。因此一個(gè)網(wǎng)頁在中等密度的屏幕上(160dpi)呈現(xiàn)出相同的可感知尺寸嵌削。如果圖形是我們網(wǎng)頁設(shè)計(jì)重要元素毛好,我們應(yīng)該更加考慮出現(xiàn)在不同密度屏幕上的縮,因?yàn)橐粋€(gè)300像素寬的圖片在一個(gè)320dpi的屏幕上將被按比例放大(每CSS像素使用更多的物理像素)苛秕,這將會(huì)導(dǎo)致偽影(模糊像素)肌访。
使用WebView構(gòu)建Web Apps
如果我們想實(shí)現(xiàn)一個(gè)web應(yīng)用程序(或只是一個(gè)網(wǎng)頁)作為一個(gè)客戶端應(yīng)用程序的一部分,我們可以使用WebView。WebView類是Android中View類的擴(kuò)展艇劫,它允許我們將網(wǎng)頁作為我們布局的一部分進(jìn)行展示吼驶。它不包含非常完善的web瀏覽器所具備的任何特性,比如導(dǎo)航控件或一個(gè)地址欄。WebView默認(rèn)能做的是顯示一個(gè)網(wǎng)頁店煞。
一個(gè)常見的場景,比如我們想要在應(yīng)用程序中展示一些信息,這些需要更新,如一個(gè)終端用戶協(xié)議或用戶指南蟹演,此時(shí)使用WebView是有益的。我們可以創(chuàng)建一個(gè)僅含有一個(gè)WebView的Activity來展示在線托管的文檔顷蟀。
另一個(gè)場景酒请,如果我們的應(yīng)用提供給用戶的數(shù)據(jù)總是需要一個(gè)網(wǎng)絡(luò)連接來檢索數(shù)據(jù),比如郵件衩椒,此時(shí)使用WebView是有用的蚌父。我們將會(huì)發(fā)現(xiàn)在應(yīng)用中使用WebView來顯示用戶的所有數(shù)據(jù)比執(zhí)行一個(gè)網(wǎng)絡(luò)請求、解析數(shù)據(jù)毛萌、布局展示更加簡單方便苟弛。我們可以為Android設(shè)備設(shè)計(jì)一個(gè)網(wǎng)頁,然后在Android應(yīng)用中實(shí)現(xiàn)一個(gè)WebView中加載這個(gè)網(wǎng)頁阁将。
向應(yīng)用中添加一個(gè)WebView
1.布局中添加WebView
2.代碼中找到WebView膏秫,并以url為參數(shù)調(diào)用WebView對象的loadUrl()方法
3.添加網(wǎng)絡(luò)權(quán)限
在WebView中使用JavaScript
如果要加載的網(wǎng)頁使用到的JavaScript,我們必須使WebView啟用JavaScript做盅。一旦JavaScript被啟用缤削,我們可以在我們的應(yīng)用代碼和JavaScript代碼之間創(chuàng)建接口。
啟用JavaScript
WebView默認(rèn)禁用JavaScript吹榴。我們可以通過WebView的WebSettings來啟用它亭敢。我們可以通過getSettings()
方法來獲取WebSettings,然后通過setJavaScriptEnabled()
方法來啟用它图筹。
例如:
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebSettings提供了大量非常有用的其他設(shè)置帅刀。例如让腹,如果我們開發(fā)一個(gè)Web應(yīng)用程序,它被設(shè)計(jì)為僅用WebView扣溺,我們可以通過setUserAgentString()
方法定義一個(gè)用戶代理字符串骇窍,然后在網(wǎng)頁中查詢這個(gè)用戶代理字符串,來驗(yàn)證用戶請求網(wǎng)頁實(shí)際是請求我們的Android應(yīng)用锥余。
JavaScript代碼綁定到Android代碼
我們可以在JavaScript代碼和客戶端的Android代碼之間創(chuàng)建一個(gè)接口腹纳,例如我們的JavaScript代碼可以調(diào)用我們的Android代碼來顯示一個(gè)Dialog,而不是使用JavaScript的alert()
方法驱犹。
在二者之間綁定接口需要調(diào)用addJavascriptInterface()
方法嘲恍,傳遞給這個(gè)方法一個(gè)類的實(shí)例來綁定到JavaScript,再傳遞一個(gè)接口名着绷,讓我們的JavaScript可以調(diào)用獲取到這個(gè)類蛔钙。
例如,在Android代碼中有如下一個(gè)類:
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可以調(diào)用的方法上添加@JavascriptInterface
注解,該方法也必須使公有的桑涎。若不添加彬向,Android 4.2或更高設(shè)備上該方法將無法執(zhí)行。
WebAppInterface類允許網(wǎng)頁使用showToast()
方法來創(chuàng)建一個(gè)吐司信息攻冷。我們可以如下將該類綁定至JavaScript娃胆。
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
這為JavaScript創(chuàng)建了一個(gè)名為Android的接口,此時(shí)等曼,我們的web應(yīng)用可以獲取WebAppInterface類了里烦。例如,下面的HTML和JavaScript當(dāng)用戶點(diǎn)擊按鈕的時(shí)候使用這個(gè)新接口創(chuàng)建了一個(gè)吐司信息胁黑。
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
</script>
在JavaScript中不需要初始化這個(gè)接口州泊,WebView自動(dòng)使它可用于您的web頁面遥皂。
提示:綁定在JavaScript的對象運(yùn)行在其他線程演训,而不再它被構(gòu)造的線程。
注意:使用addJavascriptInterface()
方法允許JavaScript控制我們的Android應(yīng)用拂募,它非常有用但也產(chǎn)生了嚴(yán)重的安全問題没讲。例如在WebView中的HTML是不可靠的(例如HTML的一部分或所有由未知的人或進(jìn)程提供)爬凑,然后攻擊者可以執(zhí)行你的包括HTML客戶端代碼嘁信,并且可能選擇攻擊任意代碼潘靖。就此,我們不應(yīng)該使用addJavascriptInterface()
方法卦溢,除非我們寫的所有HTML和JavaScript出現(xiàn)在我們的WebView中。我們也不應(yīng)該允許用戶在我們的WebView中導(dǎo)航至那些不屬于我們的網(wǎng)頁中(相反贬芥,默認(rèn)允許用戶的默認(rèn)瀏覽器應(yīng)用打開外部鏈接宣决,用戶的網(wǎng)頁瀏覽器打開所有的URL鏈接)尊沸。
處理頁面導(dǎo)航
當(dāng)用戶在我們的WebView中點(diǎn)擊一個(gè)鏈接的時(shí)候,默認(rèn)處理方式是Android運(yùn)行一個(gè)應(yīng)用來處理所有的URLs洼专。通常壶熏,是默認(rèn)網(wǎng)頁瀏覽器來打開和加載這個(gè)目標(biāo)URL棒假。然而帽哑,我們在WebView中可以復(fù)寫這個(gè)行為妻枕,使得鏈接在我們的WebView中打開。然后我們允許用戶在WebView中通過網(wǎng)頁歷史來前后導(dǎo)航至某一網(wǎng)頁蝌数。
為了打開用戶點(diǎn)擊的鏈接顶伞,僅需調(diào)用setWebViewClient()
方法為WebView提供一個(gè)WebViewClient剑梳,如下:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient());
就是這樣,然后我們的WebView就可以加載所有用戶點(diǎn)的鏈接了垢乙。
如果我們想要更多地控制被點(diǎn)擊的鏈接锨咙,需要通過繼承自定義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創(chuàng)建一個(gè)新的WebViewClient對象:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());
現(xiàn)在當(dāng)用戶點(diǎn)擊一個(gè)鏈接時(shí),系統(tǒng)調(diào)用shouldOverrideUrlLoading()
方法羊壹,來檢查這個(gè)URL的主機(jī)地址是否匹配代碼中指定的域名蓖宦。如果匹配油猫,這個(gè)方法將返回false(通常允許WebView加載這個(gè)網(wǎng)頁)。如果不匹配柠偶,創(chuàng)建一個(gè)Intent來啟動(dòng)默認(rèn)Activity來處理URL情妖。
瀏覽網(wǎng)頁的歷史
讓我們的WebView重復(fù)加載了多個(gè)URL诱担,它自動(dòng)地累積瀏覽過的歷史,我們可以可以通過goBack()
和goForward()
方法來前進(jìn)或者后退這些歷史網(wǎng)頁料睛。
如下展示了在Activity中使用設(shè)備返回鍵來回退歷史網(wǎng)頁:
@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);
}
Android 4.4的WebView(需要注意的地方)
Android4.4引進(jìn)了全新的基于Chrominum的WebView施籍。這個(gè)變化升級了WebView的性能居扒。所有的在Android4.4或更高版本的Android設(shè)備使用WebView的Apps將繼承這些優(yōu)化丑慎。