在WebView中構(gòu)建Web應(yīng)用程序(二)

如果要將Web應(yīng)用程序(或僅僅是網(wǎng)頁(yè))作為客戶端應(yīng)用程序的一部分提供,則可以使用WebView執(zhí)行此操作摧茴。 WebView類是AndroidView類的擴(kuò)展绵载,允許您將Web頁(yè)面顯示為活動(dòng)布局的一部分。它不包括完全開發(fā)的Web瀏覽器的任何功能蓬蝶,例如導(dǎo)航控件或地址欄。默認(rèn)情況下猜惋,WebView所做的就是顯示一個(gè)網(wǎng)頁(yè)丸氛。

本文檔向您展示了如何開始使用WebView以及如何執(zhí)行其他操作,例如處理頁(yè)面導(dǎo)航以及將網(wǎng)頁(yè)中的JavaScript綁定到Android應(yīng)用程序中的客戶端代碼著摔。

一缓窜、將WebView添加到您的應(yīng)用程序

要將WebView添加到應(yīng)用程序,可以在活動(dòng)布局中包含<WebView>元素谍咆,也可以將整個(gè)“活動(dòng)”窗口設(shè)置為onCreate()中的WebView禾锤。

在活動(dòng)布局中添加WebView

將以下代碼添加到活動(dòng)的布局XML文件中:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

要在WebView中加載網(wǎng)頁(yè),請(qǐng)使用loadUrl()摹察。 例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
在onCreate()中添加WebView

在活動(dòng)的onCreate()方法中向您的應(yīng)用添加WebView

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

然后加載頁(yè)面:

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 =
     "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

注意:此HTML可以執(zhí)行的操作有一些限制恩掷。 有關(guān)編碼選項(xiàng)的詳細(xì)信息,請(qǐng)參閱loadData()loadDataWithBaseURL()供嚎。

權(quán)限

在此之前黄娘,您的應(yīng)用必須能夠訪問Internet峭状。 請(qǐng)?jiān)谇鍐挝募姓?qǐng)求INTERNET權(quán)限:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

這就是顯示網(wǎng)頁(yè)的基本WebView所需的全部?jī)?nèi)容。 此外逼争,您可以通過修改以下內(nèi)容來自定義WebView

  • 使用WebChromeClient啟用全屏支持优床。 當(dāng)WebView需要更改主機(jī)應(yīng)用程序UI的權(quán)限時(shí),也會(huì)調(diào)用此類誓焦,例如創(chuàng)建或關(guān)閉窗口以及向用戶發(fā)送JavaScript對(duì)話框胆敞。
  • 處理影響內(nèi)容呈現(xiàn)的事件,例如表單提交上的錯(cuò)誤或使用WebViewClient導(dǎo)航杂伟。 您還可以使用此子類來攔截URL加載移层。
  • 通過修改WebSettings啟用JavaScript
  • 使用JavaScript訪問已注入WebViewAndroid框架對(duì)象稿壁。

二幽钢、在WebView中使用JavaScript

如果您計(jì)劃在WebView中加載的網(wǎng)頁(yè)使用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提供對(duì)您可能會(huì)發(fā)現(xiàn)有用的各種其他設(shè)置的訪問。 例如书闸,如果您正在開發(fā)專門為Android應(yīng)用程序中的WebView設(shè)計(jì)的Web應(yīng)用程序尼变,則可以使用setUserAgentString()定義自定義用戶代理字符串,然后在網(wǎng)頁(yè)中查詢自定義用戶代理以驗(yàn)證浆劲,請(qǐng)求您的網(wǎng)頁(yè)的客戶實(shí)際上是您的Android應(yīng)用嫌术。

將JavaScript代碼綁定到Android代碼

在開發(fā)專為Web應(yīng)用程序中的WebView設(shè)計(jì)的Web應(yīng)用程序時(shí),您可以在JavaScript代碼和客戶端Android代碼之間創(chuàng)建接口牌借。 例如度气,您的JavaScript代碼可以調(diào)用Android代碼中的方法來顯示Dialog,而不是使用JavaScriptalert()函數(shù)膨报。

要綁定JavaScriptAndroid代碼之間的新接口磷籍,請(qǐng)調(diào)用addJavascriptInterface(),向其傳遞一個(gè)類實(shí)例以綁定到您的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)行時(shí),您的網(wǎng)頁(yè)無法訪問該方法周循。
在此示例中谈秫,WebAppInterface類允許網(wǎng)頁(yè)使用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)建一個(gè)名為Android for JavaScript的界面该编。 此時(shí),您的Web應(yīng)用程序可以訪問WebAppInterface類硕淑。 例如课竣,以下是一些HTMLJavaScript,當(dāng)用戶單擊按鈕時(shí)置媳,使用新界面創(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會(huì)自動(dòng)將其提供給您的網(wǎng)頁(yè)。 因此拇囊,只需單擊按鈕迂曲,showAndroidToast()函數(shù)就會(huì)使用Android界面調(diào)用WebAppInterface.showToast()方法。

注意:綁定到JavaScript的對(duì)象在另一個(gè)線程中運(yùn)行寥袭,而不是在構(gòu)造它的線程中運(yùn)行路捧。

警告:使用addJavascriptInterface()可以讓JavaScript控制您的Android應(yīng)用。 這可能是一個(gè)非常有用的功能或危險(xiǎn)的安全問題传黄。 當(dāng)WebView中的HTML不可信(例如杰扫,部分或全部HTML由未知的人或進(jìn)程提供)時(shí),攻擊者可以包含執(zhí)行客戶端代碼的HTML膘掰,也可能包括攻擊者選擇的任何代碼章姓。 因此,除非您編寫了WebView中顯示的所有HTMLJavaScript识埋,否則不應(yīng)使用addJavascriptInterface()凡伊。 您也不應(yīng)允許用戶在WebView中導(dǎo)航到不屬于您自己的其他網(wǎng)頁(yè)(而是允許用戶的默認(rèn)瀏覽器應(yīng)用程序打開外部鏈接 - 默認(rèn)情況下,用戶的Web瀏覽器會(huì)打開所有URL鏈接窒舟,因此 只有在按照以下部分所述處理頁(yè)面導(dǎo)航時(shí)才要小心)系忙。

三、處理頁(yè)面導(dǎo)航

當(dāng)用戶從WebView中的網(wǎng)頁(yè)單擊鏈接時(shí)辜纲,Android的默認(rèn)行為是啟動(dòng)處理URL的應(yīng)用程序笨觅。 通常拦耐,默認(rèn)Web瀏覽器會(huì)打開并加載目標(biāo)URL耕腾。 但是,您可以為WebView覆蓋此行為杀糯,以便在WebView中打開鏈接扫俺。 然后,您可以允許用戶在WebView維護(hù)的網(wǎng)頁(yè)歷史記錄中前后導(dǎo)航固翰。

注意:出于安全考慮狼纬,系統(tǒng)的瀏覽器應(yīng)用程序不會(huì)與您的應(yīng)用共享其應(yīng)用程序數(shù)據(jù)羹呵。

要打開用戶單擊的鏈接,請(qǐng)使用setWebViewClient()WebView提供WebViewClient疗琉。 例如:

WebView myWebView =  (WebView) findViewById(R.id.webview); 
myWebView.setWebViewClient(MyWebViewClient);

現(xiàn)在冈欢,用戶在WebView中單擊加載的所有鏈接。

如果您想要更好地控制單擊鏈接的加載位置盈简,請(qǐng)創(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)建這個(gè)新WebViewClient的實(shí)例:

WebView myWebView =  (WebView) findViewById(R.id.webview); 
myWebView.setWebViewClient(new  MyWebViewClient());

現(xiàn)在,當(dāng)用戶單擊鏈接時(shí)柠贤,系統(tǒng)調(diào)用shouldOverrideUrlLoading()香浩,它會(huì)檢查URL主機(jī)是否與特定域匹配(如上所述)。 如果它匹配臼勉,則該方法返回false以便不覆蓋URL加載(它允許WebView像往常一樣加載URL)邻吭。 如果URL主機(jī)不匹配瘩蚪,則會(huì)創(chuàng)建一個(gè)Intent以啟動(dòng)處理URL的默認(rèn)活動(dòng)(解析為用戶的默認(rèn)Web瀏覽器)盛险。

瀏覽網(wǎng)頁(yè)歷史記錄

當(dāng)WebView覆蓋URL加載時(shí),它會(huì)自動(dòng)累積訪問過的網(wǎng)頁(yè)的歷史記錄实愚。 您可以使用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);  
    }  
}

如果實(shí)際存在用戶要訪問的網(wǎng)頁(yè)歷史記錄,則canGoBack()方法返回true恩闻。 同樣艺糜,您可以使用canGoForward()來檢查是否存在轉(zhuǎn)發(fā)歷史記錄。 如果您不執(zhí)行此檢查幢尚,那么一旦用戶到達(dá)歷史記錄的末尾破停,goBack()goForward()就不會(huì)執(zhí)行任何操作。

處理設(shè)備配置更改

在運(yùn)行時(shí)期間尉剩,當(dāng)設(shè)備的配置發(fā)生更改(例如真慢,用戶旋轉(zhuǎn)設(shè)備或關(guān)閉輸入法編輯器(IME)時(shí))會(huì)發(fā)生活動(dòng)狀態(tài)更改。 這些更改將導(dǎo)致銷毀WebView對(duì)象的活動(dòng)并創(chuàng)建新活動(dòng)理茎,這也會(huì)創(chuàng)建一個(gè)新的WebView對(duì)象黑界,該對(duì)象將加載銷毀對(duì)象的URL。 要修改活動(dòng)的默認(rèn)行為皂林,您可以更改其在清單中處理方向更改的方式朗鸠。

四、管理窗口

默認(rèn)情況下础倍,將忽略打開新窗口的請(qǐng)求烛占。 無論是通過JavaScript還是通過鏈接中的target屬性打開它都是如此。 您可以自定義WebChromeClient以提供打開多個(gè)窗口的行為。

警告:為了使您的應(yīng)用更安全忆家,最好防止彈出窗口和新窗口打開犹菇。 實(shí)現(xiàn)此行為的最安全方法是將“true”傳遞給setSupportMultipleWindows(),但不覆蓋setSupportMultipleWindows()依賴的onCreateWindow()方法芽卿。 但請(qǐng)記住揭芍,此邏輯還會(huì)阻止任何在其鏈接中使用target =“_ blank”的頁(yè)面加載。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卸例,一起剝皮案震驚了整個(gè)濱河市沼沈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌币厕,老刑警劉巖列另,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異旦装,居然都是意外死亡页衙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門阴绢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來店乐,“玉大人,你說我怎么就攤上這事呻袭≌0耍” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵左电,是天一觀的道長(zhǎng)廉侧。 經(jīng)常有香客問我,道長(zhǎng)篓足,這世上最難降的妖魔是什么段誊? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮栈拖,結(jié)果婚禮上连舍,老公的妹妹穿的比我還像新娘。我一直安慰自己涩哟,他們只是感情好索赏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贴彼,像睡著了一般潜腻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锻弓,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天砾赔,我揣著相機(jī)與錄音蝌箍,去河邊找鬼青灼。 笑死暴心,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杂拨。 我是一名探鬼主播专普,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼弹沽!你這毒婦竟也來了檀夹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤策橘,失蹤者是張志新(化名)和其女友劉穎炸渡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丽已,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚌堵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沛婴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吼畏。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嘁灯,靈堂內(nèi)的尸體忽然破棺而出泻蚊,到底是詐尸還是另有隱情,我是刑警寧澤丑婿,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布性雄,位于F島的核電站,受9級(jí)特大地震影響羹奉,放射性物質(zhì)發(fā)生泄漏毅贮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一尘奏、第九天 我趴在偏房一處隱蔽的房頂上張望滩褥。 院中可真熱鬧,春花似錦炫加、人聲如沸瑰煎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酒甸。三九已至,卻和暖如春赋铝,著一層夾襖步出監(jiān)牢的瞬間插勤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留农尖,地道東北人析恋。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像盛卡,于是被迫代替她去往敵國(guó)和親助隧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • assets 文件夾下的html訪問方式:"file:///android_asset/index.html" 如...
    李建彪閱讀 1,734評(píng)論 0 7
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,152評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理滑沧,服務(wù)發(fā)現(xiàn)并村,斷路器,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • (各位看官喜歡俺文字可以轉(zhuǎn)發(fā)滓技,但是不要打賞哩牍,俺收不到) 假期逃離婆婆家親戚輪番喝酒聚餐,早早返京令漂,就三個(gè)閑人姐叁,家里...
    林有錢閱讀 1,228評(píng)論 9 3
  • 今天的狀態(tài),有些飄 【一】無所適從 有點(diǎn)莫名其妙的茫然洗显。 畫了兩張黑磚外潜。 【二】挑戰(zhàn)徒手畫圓
    冉聽花開閱讀 119評(píng)論 2 2