關(guān)于webview最詳細(xì)講解(包含 h5 和android 交互)(webview_)
為什么要學(xué)習(xí)Android與H5互調(diào)?
微信,QQ空間等大量軟件都內(nèi)嵌了H5,不得不說(shuō)是一種趨勢(shì)。Android與H5互調(diào)可以讓我們的實(shí)現(xiàn)混合開(kāi)發(fā),至于混合開(kāi)發(fā)就是在一個(gè)App中內(nèi)嵌一個(gè)輕量級(jí)的瀏覽器,一部分原生的功能改為Html 5來(lái)開(kāi)發(fā)。
優(yōu)勢(shì):使用H5實(shí)現(xiàn)的功能能夠在不升級(jí)App的情況下動(dòng)態(tài)更新,而且可以在Android或iOS的App上同時(shí)運(yùn)行,節(jié)約了成本,提高了開(kāi)發(fā)效率。
原理:其實(shí)就是java代碼和JavaScript之間的調(diào)用。
開(kāi)局插入一張文章的目錄結(jié)構(gòu):
WebView簡(jiǎn)介
要實(shí)現(xiàn)Android與H5互調(diào),WebView是一個(gè)很重要的控件,WebView可以很好地幫助我們展示html頁(yè)面,所以有必要先了解一下WebView。
一丶WebView常用方法
- loadUrl
- 加載界面,其次還有LoadData和LoadDataWithBase方法
//加載assets目錄下的test.html文件webView.loadUrl(“file:///android_asset/test.html”);//加載網(wǎng)絡(luò)資源(注意要加上網(wǎng)絡(luò)權(quán)限)webView.loadUrl(“http://blog.csdn.net”);
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
- setWebViewClient(如果用戶設(shè)置了WebViewClient,則在點(diǎn)擊新的鏈接以后就不會(huì)跳轉(zhuǎn)到系統(tǒng)瀏覽器了,而是在本W(wǎng)ebView中顯示。注意:并不需要覆蓋 shouldOverrideUrlLoading 方法,同樣可以實(shí)現(xiàn)所有的鏈接都在 WebView 中打開(kāi)。)
- WebViewClient主要用來(lái)輔助WebView處理各種通知、請(qǐng)求等事件,通過(guò)setWebViewClient方法設(shè)置。以下是它的幾種常見(jiàn)用法:
- 實(shí)現(xiàn)對(duì)網(wǎng)頁(yè)中超鏈接的攔截(比如如果是極客導(dǎo)航的主頁(yè),則直接攔截轉(zhuǎn)到百度主頁(yè)):
- 當(dāng)點(diǎn)擊頁(yè)面中的鏈接后,會(huì)在WebView加載URL前回調(diào)shouldOverrideUrlLoading(WebView view, String url)方法,一般點(diǎn)擊一個(gè)鏈接此方法調(diào)用一次。
webView.setWebViewClient(new WebViewClient(){@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) { if(“http://www.jikedaohang.com/”.equals(url)) { view.loadUrl(“https://www.baidu.com/”); } return true; } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 關(guān)于shouldOverrideUrlLoading返回值的誤區(qū):網(wǎng)上很多解釋是return true代表在本W(wǎng)ebView中打開(kāi)鏈接,return false代表調(diào)用系統(tǒng)瀏覽器打開(kāi)鏈接。其實(shí)只要設(shè)置了WebViewClient,則就不會(huì)調(diào)用系統(tǒng)瀏覽器。
- 那么shouldOverrideUrlLoading的返回值到底代表什么呢?return true,則在打開(kāi)新的url時(shí)WebView就不會(huì)再加載這個(gè)url了,所有處理都需要在WebView中操作,包含加載;return false,則系統(tǒng)就認(rèn)為上層沒(méi)有做處理,接下來(lái)還是會(huì)繼續(xù)加載這個(gè)url的;默認(rèn)return false。具體的區(qū)別展示如下:
- 加載百度主頁(yè),設(shè)置WebViewClient后,重寫shouldOverrideUrlLoading(WebView view, String url)方法,第一張是返回false的截圖(點(diǎn)擊后正常跳轉(zhuǎn)),第二章是返回true的截圖(點(diǎn)擊無(wú)反應(yīng),如果希望能夠跳轉(zhuǎn),則需要我們自己進(jìn)行處理):
- 還有一點(diǎn)需要注意的是,如果我們攔截了某個(gè)url,那么return false 和 return true區(qū)別不大,所以一般建議 return false。
- 加載網(wǎng)頁(yè)時(shí)替換某個(gè)資源(比如在加載一個(gè)網(wǎng)頁(yè)時(shí),需要加載一個(gè)logo圖片,而我們想要替換這個(gè)logo圖片,用我們assets目錄下的一張圖片替代)
- 我們知道我們?cè)诩虞d一個(gè)網(wǎng)頁(yè)的同時(shí)也會(huì)加載js,css,圖片等資源,所以會(huì)多次調(diào)用shouldInterceptRequest方法,我們可以在shouldInterceptRequest中進(jìn)行圖片替換。
- 注意:shouldInterceptRequest有兩個(gè)重載:
- ①public WebResourceResponse shouldInterceptRequest (WebView view, String url) 【已過(guò)時(shí)】
- ②public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
- 這兩種方法主要是第二個(gè)參數(shù)的不同,WebResourceRequest 將能夠獲取更多的信息,提供了getUrl(),getMethod,getRequestHeaders等方法。這里主要是為了展示效果,使用了第一種回調(diào)方法。實(shí)現(xiàn)方法如下:
mWebView.setWebViewClient(new WebViewClient(){ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { WebResourceResponse response = null; if (url.contains(“logo”)) { try { InputStream logo = getAssets().open(“logo.png”); response = new WebResourceResponse(“image/png”, “UTF-8”, logo); } catch (IOException e) { e.printStackTrace(); } } return response; } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 設(shè)置開(kāi)始加載網(wǎng)頁(yè)、加載完成、加載錯(cuò)誤時(shí)處理
webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); // 開(kāi)始加載網(wǎng)頁(yè)時(shí)處理 如:顯示”加載提示” 的加載對(duì)話框 … } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 網(wǎng)頁(yè)加載完成時(shí)處理 如:讓 加載對(duì)話框 消失 … } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); // 加載網(wǎng)頁(yè)失敗時(shí)處理 如:提示失敗,或顯示新的界面 … } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 處理https請(qǐng)求,為WebView處理ssl證書(shū)設(shè)置WebView默認(rèn)是不處理https請(qǐng)求的,需要在WebViewClient子類中重寫父類的onReceivedSslError函數(shù)
webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); // 接受信任所有網(wǎng)站的證書(shū) // handler.cancel(); // 默認(rèn)操作 不處理 // handler.handleMessage(null); // 可做其他處理 } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- setWebChromeClient
- WebChromeClient主要用來(lái)輔助WebView處理Javascript的對(duì)話框、網(wǎng)站圖標(biāo)、網(wǎng)站標(biāo)題以及網(wǎng)頁(yè)加載進(jìn)度等。通過(guò)WebView的setWebChromeClient()方法設(shè)置。
- 顯示頁(yè)面加載進(jìn)度在WebChromeClient子類中重寫父類的onProgressChanged函數(shù),progress表示當(dāng)前頁(yè)面加載的進(jìn)度,為1至100的整數(shù)
webView.setWebChromeClient(new WebChromeClient() { public void onProgressChanged(WebView view, int progress) { setTitle(“頁(yè)面加載中,請(qǐng)稍候…” progress “%”); setProgress(progress * 100); if (progress == 100) { //… } } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 加快HTML網(wǎng)頁(yè)加載完成速度(默認(rèn)情況html代碼下載到WebView后,webkit開(kāi)始解析網(wǎng)頁(yè)各個(gè)節(jié)點(diǎn),發(fā)現(xiàn)有外部樣式文件或者外部腳本文件時(shí),會(huì)異步發(fā)起網(wǎng)絡(luò)請(qǐng)求下載文件,但如果在這之前也有解析到image節(jié)點(diǎn),那勢(shì)必也會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求下載相應(yīng)的圖片。在網(wǎng)絡(luò)情況較差的情況下,過(guò)多的網(wǎng)絡(luò)請(qǐng)求就會(huì)造成帶寬緊張,影響到css或js文件加載完成的時(shí)間,造成頁(yè)面空白loading過(guò)久。解決的方法就是告訴WebView先不要自動(dòng)加載圖片,等頁(yè)面finish后再發(fā)起圖片加載。)
//1.首先在WebView初始化時(shí)添加如下代碼if(Build.VERSION.SDK_INT >= 19) { /*對(duì)系統(tǒng)API在19以上的版本作了兼容。因?yàn)?.4以上系統(tǒng)在onPageFinished時(shí)再恢復(fù)圖片加載時(shí),如果存在多張圖片引用的是相同的src時(shí),會(huì)只有一個(gè)image標(biāo)簽得到加載,因而對(duì)于這樣的系統(tǒng)我們就先直接加載。*/ webView.getSettings().setLoadsImagesAutomatically(true); } else { webView.getSettings().setLoadsImagesAutomatically(false); } //2.在WebView的WebViewClient子類中重寫onPageFinished()方法添加如下代碼: @Override public void onPageFinished(WebView view, String url) { if(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(true); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- setDownloadListener
- 通常webview渲染的界面中含有可以下載文件的鏈接,點(diǎn)擊該鏈接后,應(yīng)該開(kāi)始執(zhí)行下載的操作并保存文件到本地中。
- 創(chuàng)建DownloadListener
class MyDownloadListenter implements DownloadListener{ @Override public void onDownloadStart(String url, String userAgent,String contentDisposition, String mimetype, long contentLength) { //下載任務(wù)…,主要有兩種方式 //(1)自定義下載任務(wù) //(2)調(diào)用系統(tǒng)的download的模塊 Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 給webview加入監(jiān)聽(tīng)
webview.setDownloadListener(new MyDownloadListenter());
- 1
- 1
- goBack()
- 返回上一瀏覽頁(yè)面,通過(guò)重寫onKeyDown方法實(shí)現(xiàn)點(diǎn)擊返回鍵返回上一瀏覽頁(yè)面而非退出程序
public boolean onKeyDown(int keyCode, KeyEvent event) { //其中webView.canGoBack()在webView含有一個(gè)可后退的瀏覽記錄時(shí)返回true if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) { webView.goBack(); return true; } return super.onKeyDown(keyCode, event); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
二丶WebSettings配置
- 獲取WebSettings對(duì)象
WebSettings webSettings = webView.getSettings();
- 常用設(shè)置方法
- (1)支持js
settings.setJavaScriptEnabled(true);
- (2)設(shè)置緩存方式,主要有以下幾種:
- LOAD_CACHE_ONLY: 不使用網(wǎng)絡(luò),只讀取本地緩存數(shù)據(jù)。
- LOAD_DEFAULT: 根據(jù)cache-control決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。
- LOAD_CACHE_NORMAL: API level 17中已經(jīng)廢棄, 從API level 11開(kāi)始作用同LOAD_DEFAULT模式。
- LOAD_NO_CACHE: 不使用緩存,只從網(wǎng)絡(luò)獲取數(shù)據(jù)。
- LOAD_CACHE_ELSE_NETWORK:只要本地有,無(wú)論是否過(guò)期,或者no-cache,都使用緩存中的數(shù)據(jù)。
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
- (3)開(kāi)啟DOM storage API功能(HTML5 提供的一種標(biāo)準(zhǔn)的接口,主要將鍵值對(duì)存儲(chǔ)在本地,在頁(yè)面加載完畢后可以通過(guò) JavaScript 來(lái)操作這些數(shù)據(jù)。)
settings.setDomStorageEnabled(true);
- (4)設(shè)置數(shù)據(jù)庫(kù)緩存路徑
settings.setDatabasePath(cacheDirPath);
- (5)設(shè)置Application Caches緩存目錄
settings.setAppCachePath(cacheDirPath);
- (6)設(shè)置默認(rèn)編碼
settings.setDefaultTextEncodingName(“utf-8”);
- (7)將圖片調(diào)整到適合webview的大小
settings.setUseWideViewPort(false);
- (8)支持縮放
settings.setSupportZoom(true);
- (9)支持內(nèi)容重新布局
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
- (10)多窗口
settings.supportMultipleWindows();
- (11)設(shè)置可以訪問(wèn)文件
settings.setAllowFileAccess(true);
- (12)當(dāng)webview調(diào)用requestFocus時(shí)為webview設(shè)置節(jié)點(diǎn)
settings.setNeedInitialFocus(true);
- (13)設(shè)置支持縮放
settings.setBuiltInZoomControls(true);
- (14)支持通過(guò)JS打開(kāi)新窗口
settings.setJavaScriptCanOpenWindowsAutomatically(true);
- (15)縮放至屏幕的大小
settings.setLoadWithOverviewMode(true);
- (16)支持自動(dòng)加載圖片
settings.setLoadsImagesAutomatically(true);
三丶WebViewClient 的回調(diào)方法列表
WebViewClient主要用來(lái)輔助WebView處理各種通知、請(qǐng)求等事件,通過(guò)setWebViewClient方法設(shè)置。
(1)更新歷史記錄
doUpdateVisitedHistory(WebView view, String url, boolean isReload)
(2)應(yīng)用程序重新請(qǐng)求網(wǎng)頁(yè)數(shù)據(jù)
onFormResubmission(WebView view, Message dontResend, Message resend)
(3)在加載頁(yè)面資源時(shí)會(huì)調(diào)用,每一個(gè)資源(比如圖片)的加載都會(huì)調(diào)用一次。
onLoadResource(WebView view, String url)
(4)開(kāi)始載入頁(yè)面調(diào)用,通常我們可以在這設(shè)定一個(gè)loading的頁(yè)面,告訴用戶程序在等待網(wǎng)絡(luò)響應(yīng)。
onPageStarted(WebView view, String url, Bitmap favicon)
(5)在頁(yè)面加載結(jié)束時(shí)調(diào)用。同樣道理,我們知道一個(gè)頁(yè)面載入完成,于是我們可以關(guān)閉loading 條,切換程序動(dòng)作。
onPageFinished(WebView view, String url)
(6)報(bào)告錯(cuò)誤信息
onReceivedError(WebView view, int errorCode, String description, String failingUrl)
(7)獲取返回信息授權(quán)請(qǐng)求
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host,String realm)
(8)重寫此方法可以讓webview處理https請(qǐng)求。
onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
(9)WebView發(fā)生改變時(shí)調(diào)用
onScaleChanged(WebView view, float oldScale, float newScale)
(10)Key事件未被加載時(shí)調(diào)用
onUnhandledKeyEvent(WebView view, KeyEvent event)
(11)重寫此方法才能夠處理在瀏覽器中的按鍵事件。
shouldOverrideKeyEvent(WebView view, KeyEvent event)
(12)在網(wǎng)頁(yè)跳轉(zhuǎn)時(shí)調(diào)用,這個(gè)函數(shù)我們可以做很多操作,比如我們讀取到某些特殊的URL,于是就可以不打開(kāi)地址,取消這個(gè)操作,進(jìn)行預(yù)先定義的其他操作,這對(duì)一個(gè)程序是非常必要的。
shouldOverrideUrlLoading(WebView view, String url)
(13)在加載某個(gè)網(wǎng)頁(yè)的資源的時(shí)候多次調(diào)用(已過(guò)時(shí))
shouldInterceptRequest(WebView view, String url)
(14)在加載某個(gè)網(wǎng)頁(yè)的資源的時(shí)候多次調(diào)用
shouldInterceptRequest(WebView view, WebResourceRequest request)
注意:
shouldOverrideUrlLoading在網(wǎng)頁(yè)跳轉(zhuǎn)的時(shí)候調(diào)用,且一般每跳轉(zhuǎn)一次只調(diào)用一次。
shouldInterceptRequest只要是網(wǎng)頁(yè)加載的過(guò)程中均會(huì)調(diào)用,資源加載的時(shí)候都會(huì)回調(diào)該方法,會(huì)多次調(diào)用。
四丶WebChoromeClient的回調(diào)方法列表
WebChromeClient主要用來(lái)輔助WebView處理Javascript的對(duì)話框、網(wǎng)站圖標(biāo)、網(wǎng)站標(biāo)題以及網(wǎng)頁(yè)加載進(jìn)度等。通過(guò)WebView的setWebChromeClient()方法設(shè)置。
(1)監(jiān)聽(tīng)網(wǎng)頁(yè)加載進(jìn)度
onProgressChanged(WebView view, int newProgress)
(2)監(jiān)聽(tīng)網(wǎng)頁(yè)標(biāo)題 : 比如百度頁(yè)面的標(biāo)題是“百度一下,你就知道”
onReceivedTitle(WebView view, String title)
(3)監(jiān)聽(tīng)網(wǎng)頁(yè)圖標(biāo)
onReceivedIcon(WebView view, Bitmap icon)
Java和JavaScript互調(diào)
為方便展示,使用addJavascriptInterface方式實(shí)現(xiàn)與本地js交互(存在漏洞)。也可通過(guò)其他方式實(shí)現(xiàn),比如攔截ur進(jìn)行參數(shù)解析l等。
Java調(diào)JS
- 首先是JS的一段代碼:
function javaCallJs(arg){ document.getElementById(“content”).innerHTML = (“歡迎:” arg ); }
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
- 然后是在java中調(diào)用JS中的方法
webView.loadUrl(“javascript:javaCallJs(” “‘” name “‘” “)”);
- 1
- 1
以上代碼就是調(diào)用了JS中一個(gè)叫javaCallJs(arg)的方法,并傳入了一個(gè)name參數(shù)。(具體效果下面有展示)
JS調(diào)java
- 配置Javascript接口
webView.addJavascriptInterface(new JSInterface (),”Android”);
- 1
- 2
- 1
- 2
- 實(shí)現(xiàn)Javascript接口類
class JSInterface { @JavascriptInterface public void showToast(String arg){ Toast.makeText(Mainactivity.this,arg,Toast.LENGTH_SHORT).show(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- JS中調(diào)用java代碼
<input type=”button” value=”點(diǎn)擊Android被調(diào)用” onclick=”window.Android.showToast(‘JS中傳來(lái)的參數(shù)’)”/>
- 1
- 1
window.Android.showToast(‘JS中傳來(lái)的參數(shù)’)”中的”Android”即addJavascriptInterface()中指定的,并且JS向java傳遞了參數(shù),類型為String。而showToast(String arg)會(huì)以Toast的形式彈出此參數(shù)。
java與JS互調(diào)代碼示例
先看效果圖:
不好意思,傳錯(cuò)了,是這張:
代碼非常簡(jiǎn)單,并且加了注釋,直接看代碼就可以了。
- 首先是本地的JavaAndJavaScriptCall.html文件,放在asstes目錄下
<html><head> <meta http-equiv=”Content-Type” content=”text/html;charset=UTF-8″> <script type=”text/javascript”> function javaCallJs(arg){ document.getElementById(“content”).innerHTML = (“歡迎:” arg ); } </script></head><body> <div id=”content”> 請(qǐng)?jiān)谏戏捷斎肽挠脩裘?lt;/div> <input type=”button” value=”點(diǎn)擊Android被調(diào)用” onclick=”window.Android.showToast(‘JS中傳來(lái)的參數(shù)’)”/></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
javaCallJs是java調(diào)用JS的方法,showToast方法是JS調(diào)用java的方法
- 接下來(lái)是布局文件,activity_main.xml
<?xml version=”1.0″ encoding=”utf-8″?><LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android” xmlns:tools=”http://schemas.android.com/tools” android:id=”@ id/ll_root” android:layout_width=”match_parent” android:layout_height=”match_parent” android:orientation=”vertical” tools:context=”.MainActivity”> <LinearLayout android:layout_width=”match_parent” android:layout_height=”wrap_content” android:orientation=”horizontal” android:padding=”20dp” android:background=”#000088″> <EditText android:id=”@ id/et_user” android:layout_width=”0dp” android:layout_height=”wrap_content” android:hint=”輸入WebView中要顯示的用戶名” android:background=”#008800″ android:textSize=”16sp” android:layout_weight=”1″/> <Button android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:layout_marginLeft=”40dp” android:layout_marginRight=”20dp” android:textSize=”16sp” android:text=”確定” android:onClick=”click”/> </LinearLayout> </LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
很簡(jiǎn)單,就是一個(gè)輸入框和一個(gè)確定按鈕,點(diǎn)擊按鈕會(huì)調(diào)用JS中的方法。
- MainActivity
package com.wangjian.webviewdemo; import android.annotation.SuppressLint;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.webkit.JavascriptInterface;import android.webkit.WebSettings;import android.webkit.WebView;import android.webkit.WebViewClient;import android.widget.EditText;import android.widget.LinearLayout;import android.widget.Toast; public class MainActivity extends AppCompatActivity { private WebView webView; private LinearLayout ll_root; private EditText et_user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ll_root = (LinearLayout) findViewById(R.id.ll_root); et_user = (EditText) findViewById(R.id.et_user); initWebView(); } //初始化WebView private void initWebView() { //動(dòng)態(tài)創(chuàng)建一個(gè)WebView對(duì)象并添加到LinearLayout中 webView = new WebView(getApplication()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); webView.setLayoutParams(params); ll_root.addView(webView); //不跳轉(zhuǎn)到其他瀏覽器 webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); WebSettings settings = webView.getSettings(); //支持JS settings.setJavaScriptEnabled(true); //加載本地html文件 webView.loadUrl(“file:///android_asset/JavaAndJavaScriptCall.html”); webView.addJavascriptInterface(new JSInterface(),”Android”); } //按鈕的點(diǎn)擊事件 public void click(View view){ //java調(diào)用JS方法 webView.loadUrl(“javascript:javaCallJs(” “‘” et_user.getText().toString() “‘” “)”); } //在頁(yè)面銷毀的時(shí)候?qū)ebView移除 @Override protected void onDestroy() { super.onDestroy(); ll_root.removeView(webView); webView.stopLoading(); webView.removeAllViews(); webView.destroy(); webView = null; } private class JSInterface { //JS需要調(diào)用的方法 @JavascriptInterface public void showToast(String arg){ Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
需要注意的地方
參考鏈接:安卓webview的一些坑
- webView.addJavascriptInterface()方法在API 17之前有一些漏洞(有興趣的可以參考本篇文章,WebView 遠(yuǎn)程代碼執(zhí)行漏洞淺析),所以在API 17以后,需要在JavaScript接口類的方法加上@JavascriptInterface注解。
- 仔細(xì)看的話你會(huì)發(fā)現(xiàn)我們上面的WebView對(duì)象并不是直接寫在布局文件中的,而是通過(guò)一個(gè)LinearLayout容器,使用addview(webview)動(dòng)態(tài)向里面添加的。另外需要注意創(chuàng)建webview需要使用applicationContext而不是activity的context,銷毀時(shí)不再占有activity對(duì)象,最后離開(kāi)的時(shí)候需要及時(shí)銷毀webview,onDestory()中應(yīng)該先從LinearLayout中remove掉webview,再調(diào)用webview.removeAllViews();webview.destory();
- 如果想要webView在產(chǎn)生OOM的時(shí)候不影響主進(jìn)程,可以另開(kāi)一個(gè)進(jìn)程,在androidmanifest.xml的activity標(biāo)簽里加上Android:process屬性就可以了。
- 在activity被殺死之后,依然保持webView的狀態(tài),方便用戶下次打開(kāi)的時(shí)候可以回到之前的狀態(tài)。webview支持saveState(bundle)和restoreState(bundle)方法。
- 保存狀態(tài)
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); wv.saveState(outState); Log.e(TAG, “save state…”); }
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
恢復(fù)狀態(tài)(在activity的onCreate(bundle savedInstanceState)里)
if(null!=savedInstanceState){ wv.restoreState(savedInstanceState); Log.i(TAG, “restore state”); }else{ wv.loadUrl(“http://3g.cn”); }
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
其他一些常見(jiàn)問(wèn)題:
1. WebViewClient.onPageFinished()。
你永遠(yuǎn)無(wú)法確定當(dāng)WebView調(diào)用這個(gè)方法的時(shí)候,網(wǎng)頁(yè)內(nèi)容是否真的加載完畢了。當(dāng)前正在加載的網(wǎng)頁(yè)產(chǎn)生跳轉(zhuǎn)的時(shí)候這個(gè)方法可能會(huì)被多次調(diào)用,StackOverflow上有比較具體的解釋(How to listen for a Webview finishing loading a URL in Android?), 但其中列舉的解決方法并不完美。所以當(dāng)你的WebView需要加載各種各樣的網(wǎng)頁(yè)并且需要在頁(yè)面加載完成時(shí)采取一些操作的話,可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠譜一些。
2. WebView后臺(tái)耗電問(wèn)題。
當(dāng)你的程序調(diào)用了WebView加載網(wǎng)頁(yè),WebView會(huì)自己開(kāi)啟一些線程(?),如果你沒(méi)有正確地將WebView銷毀的話,這些殘余的線程(?)會(huì)一直在后臺(tái)運(yùn)行,由此導(dǎo)致你的應(yīng)用程序耗電量居高不下。對(duì)此我采用的處理方式比較偷懶,簡(jiǎn)單又粗暴(不建議),即在Activity.onDestroy()中直接調(diào)用System.exit(0),使得應(yīng)用程序完全被移出虛擬機(jī),這樣就不會(huì)有任何問(wèn)題了。
3. 切換WebView閃屏問(wèn)題。
如果你需要在同一個(gè)ViewGroup中來(lái)回切換不同的WebView(包含了不同的網(wǎng)頁(yè)內(nèi)容)的話,你就會(huì)發(fā)現(xiàn)閃屏是不可避免的。這應(yīng)該是Android硬件加速的Bug,如果關(guān)閉硬件加速這種情況會(huì)好很多,但無(wú)法獲得很好的瀏覽體驗(yàn),你會(huì)感覺(jué)網(wǎng)頁(yè)滑動(dòng)的時(shí)候一卡一卡的,不跟手。
4. 在某些手機(jī)上,Webview有視頻時(shí),activity銷毀后,視頻資源沒(méi)有被銷毀,甚至還能聽(tīng)到在后臺(tái)播放。即便是像剛才那樣各種銷毀webview也無(wú)濟(jì)于事,解決辦法:在onDestory之前修改url為空地址。
5.WebView硬件加速導(dǎo)致頁(yè)面渲染閃爍問(wèn)題
關(guān)于Android硬件加速 開(kāi)始于Android 3.0 (API level 11),開(kāi)啟硬件加速后,WebView渲染頁(yè)面更加快速,拖動(dòng)也更加順滑。但有個(gè)副作用就是容易會(huì)出現(xiàn)頁(yè)面加載白塊同時(shí)界面閃爍現(xiàn)象。解決這個(gè)問(wèn)題的方法是設(shè)置WebView暫時(shí)關(guān)閉硬件加速 代碼如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}