WebView 開車指南之最全實(shí)用案例

ips

由于WebView的用法實(shí)在太多,如果您只是想查詢某個(gè)功能的使用——建議Ctrl+F(Commad+F)在本頁面搜索關(guān)鍵字查找。

文章給前半部分大多是方法的介紹,若嫌瑣碎可直接拖到最后看代碼演示。

Thanks for reading~!?(?ε? )?

前言

喝酒不開車,開車不喝酒。

目錄

WebView簡(jiǎn)介

WebView基本使用

WebView常用方法

WebSettings

WebViewClient

WebChromeClient

JavaScript與WebView交互

WebView加載優(yōu)化

駕照考試

上路

WebView簡(jiǎn)介

為了方便開發(fā)者實(shí)現(xiàn)在app內(nèi)展示網(wǎng)頁并與網(wǎng)頁交互的需求,Android SDK提供了WebView組件。

它繼承自AbsoluteLayout,展示網(wǎng)頁的同時(shí),也可以在其中放入其他的子View。

現(xiàn)如今,Hybrid應(yīng)用似乎占據(jù)的APP的主流類型,那么關(guān)于WebView的使用就變得越發(fā)的重要。

從Android

4.4(KitKat)開始,原本基于WebKit的WebView開始基于Chromium內(nèi)核,這一改動(dòng)大大提升了WebView組件的性能以及對(duì)HTML5,CSS3,JavaScript的支持。不過它的API卻沒有很大的改動(dòng),在兼容低版本的同時(shí)只引進(jìn)了少部分新的API,并不需要你做很大的改動(dòng)。

不過有幾點(diǎn)改變需要注意,但我嘗試著翻譯了下,發(fā)現(xiàn)還是英文原文說得好,所以我貼鏈接吧~~~

Migrating to WebView in Android 4.4

在WebView中,有幾個(gè)地方是我們可以使用來定制我們的WebView各種行為的,分別是:WebSettings、JavaScriptInterface、WebViewClient以及WebChromeClient。這些我都會(huì)在接下來的文章中一一介紹。

WebView基本使用

下面簡(jiǎn)單介紹下WebView的基本使用:

首先新建一個(gè)工程,在layout文件里放入一個(gè)WebView控件(當(dāng)然也可以通過Java代碼動(dòng)態(tài)放入,這里不演示了)

然后在Activity的onCreate方法里寫入如下代碼:

Stringurl="https://www.google.com";WebViewwebView=(WebView)findViewById(R.id.web_view);webView.loadUrl(url);

接著在AndroidManifest聲明訪問網(wǎng)絡(luò)的權(quán)限:

就,完事了~

這時(shí)運(yùn)行app,它已經(jīng)可以訪問指定地址的網(wǎng)頁了。

上面提到了WebView繼承自AbsoluteLayout,可以在其中放入一些子View,那也順手來一下。

Layout文件改為:

Activity的onCreate里加上:

Buttonbutton=(Button)findViewById(R.id.button);button.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewview){Toast.makeText(getApplicationContext(),"系好安全帶!",Toast.LENGTH_SHORT).show();}});

這時(shí),運(yùn)行app,里面就會(huì)多出一個(gè)Button~

但如果你真的運(yùn)行的話,你就會(huì)發(fā)現(xiàn),app會(huì)自動(dòng)跳到瀏覽器并打開指定的網(wǎng)頁,而并非在app內(nèi)展示網(wǎng)頁,那這就與我們的初衷背道而馳了,那么要如何實(shí)現(xiàn)網(wǎng)頁在App內(nèi)打開呢?這就引出了下面的章節(jié)會(huì)提到的東西:WebViewClient。我先將代碼貼出,具體實(shí)現(xiàn)原理留到下節(jié)說明。

最終XML布局就如上面那樣,Java代碼(最終)如下:

publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Stringurl="https://www.google.com";WebViewwebView=(WebView)findViewById(R.id.web_view);webView.loadUrl(url);webView.setWebViewClient(newWebViewClient(){@OverridepublicbooleanshouldOverrideUrlLoading(WebViewview,WebResourceRequestrequest){view.loadUrl(request.toString());returntrue;}});Buttonbutton=(Button)findViewById(R.id.button);button.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewview){Toast.makeText(getApplicationContext(),"系好安全帶!",Toast.LENGTH_SHORT).show();}});}}

效果圖:效果視頻

WebView簡(jiǎn)介

接下來再介紹一些WebView的常用方法,具體演示會(huì)在后面章節(jié)的代碼里統(tǒng)一展示。

String getUrl():獲取當(dāng)前頁面的URL。

reload():重新reload當(dāng)前的URL,即刷新。

boolean canGoBack():用來確認(rèn)WebView里是否還有可回退的歷史記錄。通常我們會(huì)在WebView里重寫返回鍵的點(diǎn)擊事件,通過該方法判斷WebView里是否還有歷史記錄,若有則返回上一頁。

boolean canGoForward():用來確認(rèn)WebView是否還有可向前的歷史記錄。

boolean canGoBackOrForward(int steps):以當(dāng)前的頁面為起始點(diǎn),用來確認(rèn)WebView的歷史記錄是否足以后退或前進(jìn)給定的步數(shù),正數(shù)為前進(jìn),負(fù)數(shù)為后退。

goBack():在WebView歷史記錄后退到上一項(xiàng)。

goForward():在WebView歷史記錄里前進(jìn)到下一項(xiàng)。

goBackOrForward(int steps):以當(dāng)前頁面為起始點(diǎn),前進(jìn)或后退歷史記錄中指定的步數(shù),正數(shù)為前進(jìn),負(fù)數(shù)為后退。

clearCache(boolean includeDiskFiles):清空網(wǎng)頁訪問留下的緩存數(shù)據(jù)。需要注意的時(shí),由于緩存是全局的,所以只要是WebView用到的緩存都會(huì)被清空,即便其他地方也會(huì)使用到。該方法接受一個(gè)參數(shù),從命名即可看出作用。若設(shè)為false,則只清空內(nèi)存里的資源緩存,而不清空磁盤里的。

clearHistory():清除當(dāng)前webview訪問的歷史記錄。

clearFormData():清除自動(dòng)完成填充的表單數(shù)據(jù)。需要注意的是,該方法僅僅清除當(dāng)前表單域自動(dòng)完成填充的表單數(shù)據(jù),并不會(huì)清除WebView存儲(chǔ)到本地的數(shù)據(jù)。

onPause():當(dāng)頁面被失去焦點(diǎn)被切換到后臺(tái)不可見狀態(tài),需要執(zhí)行onPause操作,該操作會(huì)通知內(nèi)核安全地暫停所有動(dòng)作,比如動(dòng)畫的執(zhí)行或定位的獲取等。需要注意的是該方法并不會(huì)暫停JavaScript的執(zhí)行,若要暫停JavaScript的執(zhí)行請(qǐng)使用接下來的這個(gè)方法。

onResume():在先前調(diào)用onPause()后,我們可以調(diào)用該方法來恢復(fù)WebView的運(yùn)行。

pauseTimers():該方法面向全局整個(gè)應(yīng)用程序的webview,它會(huì)暫停所有webview的layout,parsing,JavaScript Timer。當(dāng)程序進(jìn)入后臺(tái)時(shí),該方法的調(diào)用可以降低CPU功耗。

resumeTimers():恢復(fù)pauseTimers時(shí)的所有操作。

destroy():銷毀WebView。需要注意的是:這個(gè)方法的調(diào)用應(yīng)在WebView從父容器中被remove掉之后。我們可以手動(dòng)地調(diào)用

rootLayout.removeView(webView);webView.destroy();

getScrollY():該方法返回的當(dāng)前可見區(qū)域的頂端距整個(gè)頁面頂端的距離,也就是當(dāng)前內(nèi)容滾動(dòng)的距離。

getHeight():方法都返回當(dāng)前WebView這個(gè)容器的高度。其實(shí)以上兩個(gè)方法都屬于View。

getContentHeight():該方法返回整個(gè)HTML頁面的高度,但該高度值并不等同于當(dāng)前整個(gè)頁面的高度,因?yàn)閃ebView有縮放功能, 所以當(dāng)前整個(gè)頁面的高度實(shí)際上應(yīng)該是原始HTML的高度再乘上縮放比例。因此,準(zhǔn)確的判斷方法應(yīng)該是

if(webView.getContentHeight()*webView.getScale()==(webView.getHeight()+webView.getScrollY())){//已經(jīng)處于底端}if(webView.getScrollY()==0){//處于頂端}

pageUp(boolean top):將WebView展示的頁面滑動(dòng)至頂部。

pageDown(boolean bottom):將WebView展示的頁面滑動(dòng)至底部。

WebSettings

WebSettings是用來管理WebView配置的類。當(dāng)WebView第一次創(chuàng)建時(shí),內(nèi)部會(huì)包含一個(gè)默認(rèn)配置的集合。若我們想更改這些配置,便可以通過WebSettings里的方法來進(jìn)行設(shè)置。

WebSettings對(duì)象可以通過WebView.getSettings()獲得,它的生命周期是與它的WebView本身息息相關(guān)的,如果WebView被銷毀了,那么任何由WebSettings調(diào)用的方法也同樣不能使用。

獲取WebSettings對(duì)象

WebSettingswebSettings=webView.getSettings();

WebSettings常用方法

(幾乎所有的set方法都有相應(yīng)的get方法,這里就只介紹set了。另,所有未寫方法返回值類型的皆為空類型

setJavaScriptEnabled(boolean flag):設(shè)置WebView是否可以運(yùn)行JavaScript。

setJavaScriptCanOpenWindowsAutomatically(boolean flag):設(shè)置WebView是否可以由JavaScript自動(dòng)打開窗口,默認(rèn)為false,通常與JavaScript的window.open()配合使用。

setAllowFileAccess(boolean allow):?jiǎn)⒂没蚪肳ebView訪問文件數(shù)據(jù)。

setBlockNetworkImage(boolean flag):禁止或允許WebView從網(wǎng)絡(luò)上加載圖片。需要注意的是,如果設(shè)置是從禁止到允許的轉(zhuǎn)變的話,圖片數(shù)據(jù)并不會(huì)在設(shè)置改變后立刻去獲取,而是在WebView調(diào)用reload()的時(shí)候才會(huì)生效。

這個(gè)時(shí)候,需要確保這個(gè)app擁有訪問Internet的權(quán)限,否則會(huì)拋出安全異常。

通常沒有禁止圖片加載的需求的時(shí)候,完全不用管這個(gè)方法,因?yàn)楫?dāng)我們的app擁有訪問Internet的權(quán)限時(shí),這個(gè)flag的默認(rèn)值就是false。

setSupportZoom(boolean support):設(shè)置是否支持縮放。

setBuiltInZoomControls(boolean enabled):顯示或不顯示縮放按鈕(wap網(wǎng)頁不支持)。

setSupportMultipleWindows(boolean support):設(shè)置WebView是否支持多窗口。

setLayoutAlgorithm(WebSettings.LayoutAlgorithm l):指定WebView的頁面布局顯示形式,調(diào)用該方法會(huì)引起頁面重繪。默認(rèn)值為L(zhǎng)ayoutAlgorithm#NARROW_COLUMNS。

setNeedInitialFocus(boolean flag):通知WebView是否需要設(shè)置一個(gè)節(jié)點(diǎn)獲取焦點(diǎn)當(dāng)WebView#requestFocus(int,android.graphics.Rect)被調(diào)用時(shí),默認(rèn)為true。

setAppCacheEnabled(boolean flag):?jiǎn)⒂没蚪脩?yīng)用緩存。

setAppCachePath(String appCachePath):設(shè)置應(yīng)用緩存路徑,這個(gè)路徑必須是可以讓app寫入文件的。該方法應(yīng)該只被調(diào)用一次,重復(fù)調(diào)用會(huì)被無視~

setCacheMode(int mode)用來設(shè)置WebView的緩存模式。當(dāng)我們加載頁面或從上一個(gè)頁面返回的時(shí)候,會(huì)按照設(shè)置的緩存模式去檢查并使用(或不使用)緩存。

緩存模式有四種:

LOAD_DEFAULT:默認(rèn)的緩存使用模式。在進(jìn)行頁面前進(jìn)或后退的操作時(shí),如果緩存可用并未過期就優(yōu)先加載緩存,否則從網(wǎng)絡(luò)上加載數(shù)據(jù)。這樣可以減少頁面的網(wǎng)絡(luò)請(qǐng)求次數(shù)。

LOAD_CACHE_ELSE_NETWORK:只要緩存可用就加載緩存,哪怕它們已經(jīng)過期失效。如果緩存不可用就從網(wǎng)絡(luò)上加載數(shù)據(jù)。

LOAD_NO_CACHE:不加載緩存,只從網(wǎng)絡(luò)加載數(shù)據(jù)。

LOAD_CACHE_ONLY:不從網(wǎng)絡(luò)加載數(shù)據(jù),只從緩存加載數(shù)據(jù)。

通常我們可以根據(jù)網(wǎng)絡(luò)情況將這幾種模式結(jié)合使用,比如有網(wǎng)的時(shí)候使用LOAD_DEFAULT,離線時(shí)使用LOAD_CACHE_ONLY、LOAD_CACHE_ELSE_NETWORK,讓用戶不至于在離線時(shí)啥都看不到。

setDatabaseEnabled(boolean flag):?jiǎn)⒂没蚪脭?shù)據(jù)庫緩存。

setDomStorageEnabled(boolean flag):?jiǎn)⒂没蚪肈OM緩存。

setUserAgentString(String ua):設(shè)置WebView的UserAgent值。

setDefaultEncodingName(String encoding):設(shè)置編碼格式,通常都設(shè)為“UTF-8”。

setStandardFontFamily(String font):設(shè)置標(biāo)準(zhǔn)的字體族,默認(rèn)“sans-serif”。

setCursiveFontFamily:設(shè)置草書字體族,默認(rèn)“cursive”。

setFantasyFontFamily:設(shè)置CursiveFont字體族,默認(rèn)“cursive”。

setFixedFontFamily:設(shè)置混合字體族,默認(rèn)“monospace”。

setSansSerifFontFamily:設(shè)置梵文字體族,默認(rèn)“sans-serif”。

setSerifFontFamily:設(shè)置襯線字體族,默認(rèn)“sans-serif”

setDefaultFixedFontSize(int size):設(shè)置默認(rèn)填充字體大小,默認(rèn)16,取值區(qū)間為[1-72],超過范圍,使用其上限值。

setDefaultFontSize(int size):設(shè)置默認(rèn)字體大小,默認(rèn)16,取值區(qū)間[1-72],超過范圍,使用其上限值。

setMinimumFontSize:設(shè)置最小字體,默認(rèn)8. 取值區(qū)間[1-72],超過范圍,使用其上限值。

setMinimumLogicalFontSize:設(shè)置最小邏輯字體,默認(rèn)8. 取值區(qū)間[1-72],超過范圍,使用其上限值。

以上就是一些WebSettings的常用方法,具體的使用以及一些緩存的問題會(huì)在接下來的代碼以及文章中有更加直觀的說明。

WebViewClient

從名字上不難理解,這個(gè)類就像WebView的委托人一樣,是幫助WebView處理各種通知和請(qǐng)求事件的,我們可以稱他為WebView的“內(nèi)政大臣”。

onLoadResource(WebView view, String url):該方法在加載頁面資源時(shí)會(huì)回調(diào),每一個(gè)資源(比如圖片)的加載都會(huì)調(diào)用一次。

onPageStarted(WebView view, String url, Bitmap favicon):該方法在WebView開始加載頁面且僅在Main frame loading(即整頁加載)時(shí)回調(diào),一次Main frame的加載只會(huì)回調(diào)該方法一次。我們可以在這個(gè)方法里設(shè)定開啟一個(gè)加載的動(dòng)畫,告訴用戶程序在等待網(wǎng)絡(luò)的響應(yīng)。

onPageFinished(WebView view, String url):該方法只在WebView完成一個(gè)頁面加載時(shí)調(diào)用一次(同樣也只在Main frame loading時(shí)調(diào)用),我們可以可以在此時(shí)關(guān)閉加載動(dòng)畫,進(jìn)行其他操作。

onReceivedError(WebView view, WebResourceRequest request, WebResourceError error):該方法在web頁面加載錯(cuò)誤時(shí)回調(diào),這些錯(cuò)誤通常都是由無法與服務(wù)器正常連接引起的,最常見的就是網(wǎng)絡(luò)問題。這個(gè)方法有兩個(gè)地方需要注意:

1.這個(gè)方法只在與服務(wù)器無法正常連接時(shí)調(diào)用,類似于服務(wù)器返回錯(cuò)誤碼的那種錯(cuò)誤(即HTTP ERROR),該方法是不會(huì)回調(diào)的,因?yàn)槟阋呀?jīng)和服務(wù)器正常連接上了(全怪官方文檔(︶^︶));

2.這個(gè)方法是新版本的onReceivedError()方法,從API23開始引進(jìn),與舊方法onReceivedError(WebView view,int errorCode,String description,String failingUrl)不同的是,新方法在頁面局部加載發(fā)生錯(cuò)誤時(shí)也會(huì)被調(diào)用(比如頁面里兩個(gè)子Tab或者一張圖片)。這就意味著該方法的調(diào)用頻率可能會(huì)更加頻繁,所以我們應(yīng)該在該方法里執(zhí)行盡量少的操作。

onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse):上一個(gè)方法提到onReceivedError并不會(huì)在服務(wù)器返回錯(cuò)誤碼時(shí)被回調(diào),那么當(dāng)我們需要捕捉HTTP ERROR并進(jìn)行相應(yīng)操作時(shí)應(yīng)該怎么辦呢?API23便引入了該方法。當(dāng)服務(wù)器返回一個(gè)HTTP ERROR并且它的status code>=400時(shí),該方法便會(huì)回調(diào)。這個(gè)方法的作用域并不局限于Main Frame,任何資源的加載引發(fā)HTTP ERROR都會(huì)引起該方法的回調(diào),所以我們也應(yīng)該在該方法里執(zhí)行盡量少的操作,只進(jìn)行非常必要的錯(cuò)誤處理等。

onReceivedSslError(WebView view, SslErrorHandler handler, SslError error):當(dāng)WebView加載某個(gè)資源引發(fā)SSL錯(cuò)誤時(shí)會(huì)回調(diào)該方法,這時(shí)WebView要么執(zhí)行handler.cancel()取消加載,要么執(zhí)行handler.proceed()方法繼續(xù)加載(默認(rèn)為cancel)。需要注意的是,這個(gè)決定可能會(huì)被保留并在將來再次遇到SSL錯(cuò)誤時(shí)執(zhí)行同樣的操作。

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request):當(dāng)WebView需要請(qǐng)求某個(gè)數(shù)據(jù)時(shí),這個(gè)方法可以攔截該請(qǐng)求來告知app并且允許app本身返回一個(gè)數(shù)據(jù)來替代我們?cè)疽虞d的數(shù)據(jù)。

比如你對(duì)web的某個(gè)js做了本地緩存,希望在加載該js時(shí)不再去請(qǐng)求服務(wù)器而是可以直接讀取本地緩存的js,這個(gè)方法就可以幫助你完成這個(gè)需求。你可以寫一些邏輯檢測(cè)這個(gè)request,并返回相應(yīng)的數(shù)據(jù),你返回的數(shù)據(jù)就會(huì)被WebView使用,如果你返回null,WebView會(huì)繼續(xù)向服務(wù)器請(qǐng)求。

boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request):哈~ 終于到了這個(gè)方法,在最開始的基礎(chǔ)演示時(shí)我們用到了這個(gè)方法。從實(shí)踐中我們知道,當(dāng)我們沒有給WebView提供WebViewClient時(shí),WebView如果要加載一個(gè)url會(huì)向ActivityManager尋求一個(gè)適合的處理者來加載該url(比如系統(tǒng)自帶的瀏覽器),這通常是我們不想看到的。于是我們需要給WebView提供一個(gè)WebViewClient,并重寫該方法返回true來告知WebView url的加載就在app中進(jìn)行。這時(shí)便可以實(shí)現(xiàn)在app內(nèi)訪問網(wǎng)頁。

onScaleChanged(WebView view, float oldScale, float newScale):當(dāng)WebView得頁面Scale值發(fā)生改變時(shí)回調(diào)。

boolean shouldOverrideKeyEvent(WebView view, KeyEvent event):默認(rèn)值為false,重寫此方法并return true可以讓我們?cè)赪ebView內(nèi)處理按鍵事件。

WebChromeClient

如果說WebViewClient是幫助WebView處理各種通知、請(qǐng)求事件的“內(nèi)政大臣”的話,那么WebChromeClient就是輔助WebView處理Javascript的對(duì)話框,網(wǎng)站圖標(biāo),網(wǎng)站title,加載進(jìn)度等偏外部事件的“外交大臣”。

onProgressChanged(WebView view, int newProgress):當(dāng)頁面加載的進(jìn)度發(fā)生改變時(shí)回調(diào),用來告知主程序當(dāng)前頁面的加載進(jìn)度。

onReceivedIcon(WebView view, Bitmap icon):用來接收web頁面的icon,我們可以在這里將該頁面的icon設(shè)置到Toolbar。

onReceivedTitle(WebView view, String title):用來接收web頁面的title,我們可以在這里將頁面的title設(shè)置到Toolbar。

以下兩個(gè)方法是為了支持web頁面進(jìn)入全屏模式而存在的(比如播放視頻),如果不實(shí)現(xiàn)這兩個(gè)方法,該web上的內(nèi)容便不能進(jìn)入全屏模式。

onShowCustomView(View view, WebChromeClient.CustomViewCallback callback):該方法在當(dāng)前頁面進(jìn)入全屏模式時(shí)回調(diào),主程序必須提供一個(gè)包含當(dāng)前web內(nèi)容(視頻 or Something)的自定義的View。

onHideCustomView():該方法在當(dāng)前頁面退出全屏模式時(shí)回調(diào),主程序應(yīng)在這時(shí)隱藏之前show出來的View。

Bitmap getDefaultVideoPoster():當(dāng)我們的Web頁面包含視頻時(shí),我們可以在HTML里為它設(shè)置一個(gè)預(yù)覽圖,WebView會(huì)在繪制頁面時(shí)根據(jù)它的寬高為它布局。而當(dāng)我們處于弱網(wǎng)狀態(tài)下時(shí),我們沒有比較快的獲取該圖片,那WebView繪制頁面時(shí)的gitWidth()方法就會(huì)報(bào)出空指針異常~ 于是app就crash了。。

這時(shí)我們就需要重寫該方法,在我們尚未獲取web頁面上的video預(yù)覽圖時(shí),給予它一個(gè)本地的圖片,避免空指針的發(fā)生。

View getVideoLoadingProgressView():重寫該方法可以在視頻loading時(shí)給予一個(gè)自定義的View,可以是加載圓環(huán) or something。

boolean onJsAlert(WebView view, String url, String message, JsResult result):處理Javascript中的Alert對(duì)話框。

boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result):處理Javascript中的Prompt對(duì)話框。

boolean onJsConfirm(WebView view, String url, String message, JsResult result):處理Javascript中的Confirm對(duì)話框

boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams):該方法在用戶進(jìn)行了web上某個(gè)需要上傳文件的操作時(shí)回調(diào)。我們應(yīng)該在這里打開一個(gè)文件選擇器,如果要取消這個(gè)請(qǐng)求我們可以調(diào)用filePathCallback.onReceiveValue(null)并返回true。

onPermissionRequest(PermissionRequest request):該方法在web頁面請(qǐng)求某個(gè)尚未被允許或拒絕的權(quán)限時(shí)回調(diào),主程序在此時(shí)調(diào)用grant(String [])deny()方法。如果該方法沒有被重寫,則默認(rèn)拒絕web頁面請(qǐng)求的權(quán)限。

onPermissionRequestCanceled(PermissionRequest request):該方法在web權(quán)限申請(qǐng)權(quán)限被取消時(shí)回調(diào),這時(shí)應(yīng)該隱藏任何與之相關(guān)的UI界面。

Js與WebView交互

既然嗨鳥應(yīng)用大行其道,那么毫無疑問Android與JavaScript的交互我們也必須了解清楚,下面來介紹一下JavaScript與Android是如何互相調(diào)用的。

利用WebView調(diào)用網(wǎng)頁上的JavaScript代碼

在WebView中調(diào)用Js的基本格式為webView.loadUrl("javascript:methodName(parameterValues)");

現(xiàn)有以下這段JavaScript代碼

functionreadyToGo(){alert("Hello")}functionalertMessage(message){alert(message)}functiongetYourCar(){return"Car";}

1. WebView調(diào)用JavaScript無參無返回值函數(shù)

Stringcall="javascript:readyToGo()";webView.loadUrl(call);

2. WebView調(diào)用JavScript有參無返回值函數(shù)

Stringcall="javascript:alertMessage(\""+"content"+"\")";webView.loadUrl(call);

3. WebView調(diào)用JavaScript有參數(shù)有返回值的函數(shù)

@TargetApi(Build.VERSION_CODES.KITKAT)privatevoidevaluateJavaScript(WebViewwebView){webView.evaluateJavascript("getYourCar()",newValueCallback(){@OverridepublicvoidonReceiveValue(Strings){Log.d("findCar",s);}});}

JavaScript通過WebView調(diào)用Java代碼

從API19開始,Android提供了@JavascriptInterface對(duì)象注解的方式來建立起Javascript對(duì)象和Android原生對(duì)象的綁定,提供給JavScript調(diào)用的函數(shù)必須帶有@JavascriptInterface。

演示一 JavaScript調(diào)用Android Toast方法

1. 編寫Java原生方法并用使用@JavascriptInterface注解

@JavascriptInterfacepublicvoidshow(Strings){Toast.makeText(getApplication(),s,Toast.LENGTH_SHORT).show();}

2.注冊(cè)JavaScriptInterface

webView.addJavascriptInterface(this,"android");

addJavascriptInterface的作用是把this所代表的類映射為JavaScript中的android對(duì)象。

3.編寫JavaScript代碼

functiontoastClick(){window.android.show("JavaScript called~!");}

演示二 JavaScript調(diào)用有返回值的Java方法

1.定義一個(gè)帶返回值的Java方法,并使用@JavaInterface:

@JavaInterfacepublicStringgetMessage(){return"Hello,boy~";}

2.添加JavaScript的映射

webView.addJavaScriptInterface(this,"Android");

3.通過JavaScript調(diào)用Java方法

functionshowHello(){varstr=window.Android.getMessage();console.log(str);}

以上就是Js與WebView交互的一些介紹,希望能對(duì)你有幫助。

WebView加載優(yōu)化

當(dāng)WebView的使用頻率變得頻繁的時(shí)候,對(duì)于其各方面的優(yōu)化就變得逐漸重要了起來??梢灾赖氖?,我們每加載一個(gè)

H5頁面,都會(huì)有很多的請(qǐng)求。除了HTML主URL自身的請(qǐng)求外,HTML外部引用的 JS、CSS、字體文件、圖片都是一個(gè)個(gè)獨(dú)立的HTTP

請(qǐng)求,雖然請(qǐng)求是并發(fā)的,但當(dāng)網(wǎng)頁整體數(shù)量達(dá)到一定程度的時(shí)候,再加上瀏覽器解析、渲染的時(shí)間,Web整體的加載時(shí)間變得很長(zhǎng)。同時(shí)請(qǐng)求文件越多,消耗的流量也會(huì)越多。那么對(duì)于加載的優(yōu)化就變得非常重要,這方面的經(jīng)驗(yàn)我也沒有什么別的,大概三個(gè)方面:

一個(gè),就是資源本地化的問題

首先可以明確的是,以目前的網(wǎng)絡(luò)條件,通過網(wǎng)絡(luò)去服務(wù)器獲取資源的速度是遠(yuǎn)遠(yuǎn)比不上從本地讀取的。談?wù)摳鞣N優(yōu)化策略其實(shí)恰恰忽略了“需要加載”才是阻擋速度提升的最大絆腳石。所以我們的思路一,就是將一些較重的資源比如js、css、圖片甚至HTML本身進(jìn)行本地化處理,在每次加載到這些資源的時(shí)候,從本地讀取進(jìn)行加載,可以簡(jiǎn)單記憶為“存·取·更”。

具體實(shí)現(xiàn)思路為:

“存”——將上述重量級(jí)資源打包進(jìn)apk文件,每次加載相應(yīng)文件時(shí)時(shí)從本地取即可。也可不打包,在第一次加載時(shí)以及接下來的若干間隔時(shí)間里動(dòng)態(tài)下載存儲(chǔ),將所有的資源文件都存在Android的asset目錄下;

“取”——重寫WebViewClient的WebResourceResponse? shouldInterceptRequest(WebView view, WebResourceRequest request)方法,通過一定的判別方法(例如正則表達(dá)式)攔截相應(yīng)的請(qǐng)求,從本地讀取相應(yīng)資源并返回;

“更”——建立起Cache Control機(jī)制,定期或使用API通知的形式控制本地資源的更新,保證本地資源是最新和可用的。

這里附上一篇博客鏈接,非常棒可供參考:caching-web-resources-in-the-android-device

第二個(gè),就是緩存的問題

倘若你不采用或不完全采用第一條資源本地化的思路,那么你的WebView緩存是必須要開啟的(雖然這一思路和第一條有重合的地方)。

WebSettingssettings=webView.getSettings();settings.setAppCacheEnabled(true);settings.setDatabaseEnabled(true);settings.setDomStorageEnabled(true);//開啟DOM緩存settings.setCacheMode(WebSettings.LOAD_DEFAULT);

在網(wǎng)絡(luò)正常時(shí),采用默認(rèn)緩存策略,在緩存可獲取并且沒有過期的情況下加載緩存,否則通過網(wǎng)絡(luò)獲取資源以減少頁面的網(wǎng)絡(luò)請(qǐng)求次數(shù)。

這里值得提起的是,我們經(jīng)常在app里用WebView展示頁面時(shí),并不想讓用戶覺得他是在訪問一個(gè)網(wǎng)頁。因?yàn)樘热粑覀兊腶pp里網(wǎng)頁非常多,而我們給用戶的感覺又都像在訪問網(wǎng)頁的話,我們的app便失去了意義。(我的意思是為什么用戶不直接使用瀏覽器呢?)

所以這時(shí),離線緩存的問題就值得我們注意。我們需要讓用戶在沒有網(wǎng)的時(shí)候,依然能夠操作我們的app,而不是面對(duì)一個(gè)和瀏覽器里的網(wǎng)絡(luò)錯(cuò)誤一樣的頁面,哪怕他能進(jìn)行的操作十分有限。

這里我的思路是,在開啟緩存的前提下,WebView在加載頁面時(shí)檢測(cè)網(wǎng)絡(luò)變化,倘若在加載頁面時(shí)用戶的網(wǎng)絡(luò)突然斷掉,我們應(yīng)當(dāng)更改WebView的緩存策略。

ConnectivityManagerconnectivityManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfonetworkInfo=connectivityManager.getActiveNetworkInfo();if(networkInfo.isAvailable()){settings.setCacheMode(WebSettings.LOAD_DEFAULT);//網(wǎng)絡(luò)正常時(shí)使用默認(rèn)緩存策略}else{settings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);//網(wǎng)絡(luò)不可用時(shí)只使用緩存}

既然有緩存,就要有緩存控制,與一相似的是我們也要建立緩存控制機(jī)制,定期或接受服務(wù)器通知來進(jìn)行緩存的清空或更新。

第三個(gè),就是延遲加載和執(zhí)行js

在WebView中,onPageFinished()的回調(diào)意味著頁面加載的完成。但該方法會(huì)在JavScript腳本執(zhí)行完成后才會(huì)觸發(fā),倘若我們要加載的頁面使用了JQuery,會(huì)在處理完DOM對(duì)象,執(zhí)行完$(document).ready(function()

{})后才會(huì)渲染并顯示頁面。這是不可接受的,所以我們需要對(duì)Js進(jìn)行延遲加載,當(dāng)然這部分是Web前端的工作。

如果說還有什么

那就是JsBridge一律不得濫用,這個(gè)對(duì)頁面加載的完成速度是有很大影響的,倘若一個(gè)頁面很多操作都通過JSbridge來控制,再怎么優(yōu)化也無濟(jì)于事(因?yàn)楫吘褂心敲炊嗖僮饕獙?shí)際執(zhí)行)。同時(shí)要注意的是,不管你是否對(duì)資源進(jìn)行緩存,都請(qǐng)將資源在服務(wù)器端進(jìn)行壓縮。因?yàn)闊o論是資源的獲取和更新,都是要從服務(wù)器獲取的,所以對(duì)于資源文件的壓縮其實(shí)是最直接也最應(yīng)該做的事情之一,但是一般服務(wù)器端都會(huì)做好,所以主要就是上面這三件事。

駕照考試

介紹了這么多,希望能對(duì)你有點(diǎn)幫助。接下來時(shí)純實(shí)戰(zhàn)時(shí)間,我會(huì)將上面所介紹的很多知識(shí)點(diǎn)在接下來的代碼里實(shí)際應(yīng)用一遍,希望能夠帶給你更加直觀的使用感受。

Java部分

publicclassMainActivityextendsAppCompatActivity{privateWebViewmWebView;privateProgressBarmProgressbar;privateToolbarmToolbar;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){supportRequestWindowFeature(Window.FEATURE_NO_TITLE);super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initAppBar();//初始化ToolbarinitWebView();//初始化WebViewinitWebSettings();//初始化WebSettingsinitWebViewClient();//初始化WebViewClientinitWebChromeClient();//初始化WebChromeClient}privatevoidinitAppBar(){mToolbar=(Toolbar)findViewById(R.id.toolbar);mToolbar.setTitle("載入中..");mToolbar.setTitleTextColor(getResources().getColor(R.color.colorWhite));setSupportActionBar(mToolbar);getSupportActionBar().setDisplayHomeAsUpEnabled(false);}privatevoidinitWebView(){mWebView=(WebView)findViewById(R.id.web_view);mProgressbar=(ProgressBar)findViewById(R.id.progress_bar);Stringurl="https://www.google.com";mWebView.loadUrl(url);}privatevoidinitWebSettings(){WebSettingssettings=mWebView.getSettings();//支持獲取手勢(shì)焦點(diǎn)mWebView.requestFocusFromTouch();//支持JSsettings.setJavaScriptEnabled(true);//支持插件settings.setPluginState(WebSettings.PluginState.ON);//設(shè)置適應(yīng)屏幕settings.setUseWideViewPort(true);settings.setLoadWithOverviewMode(true);//支持縮放settings.setSupportZoom(false);//隱藏原生的縮放控件settings.setDisplayZoomControls(false);//支持內(nèi)容重新布局settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);settings.supportMultipleWindows();settings.setSupportMultipleWindows(true);//設(shè)置緩存模式settings.setDomStorageEnabled(true);settings.setDatabaseEnabled(true);settings.setCacheMode(WebSettings.LOAD_DEFAULT);settings.setAppCacheEnabled(true);settings.setAppCachePath(mWebView.getContext().getCacheDir().getAbsolutePath());//設(shè)置可訪問文件settings.setAllowFileAccess(true);//當(dāng)webview調(diào)用requestFocus時(shí)為webview設(shè)置節(jié)點(diǎn)settings.setNeedInitialFocus(true);//支持自動(dòng)加載圖片if(Build.VERSION.SDK_INT>=19){settings.setLoadsImagesAutomatically(true);}else{settings.setLoadsImagesAutomatically(false);}settings.setNeedInitialFocus(true);//設(shè)置編碼格式settings.setDefaultTextEncodingName("UTF-8");}privatevoidinitWebViewClient(){mWebView.setWebViewClient(newWebViewClient(){//頁面開始加載時(shí)@OverridepublicvoidonPageStarted(WebViewview,Stringurl,Bitmapfavicon){super.onPageStarted(view,url,favicon);mProgressbar.setVisibility(View.VISIBLE);}//頁面完成加載時(shí)@OverridepublicvoidonPageFinished(WebViewview,Stringurl){super.onPageFinished(view,url);mProgressbar.setVisibility(View.GONE);}//是否在WebView內(nèi)加載新頁面@OverridepublicbooleanshouldOverrideUrlLoading(WebViewview,WebResourceRequestrequest){view.loadUrl(request.toString());returntrue;}//網(wǎng)絡(luò)錯(cuò)誤時(shí)回調(diào)的方法@OverridepublicvoidonReceivedError(WebViewview,WebResourceRequestrequest,WebResourceErrorerror){super.onReceivedError(view,request,error);/**

? ? ? ? ? ? ? * 在這里寫網(wǎng)絡(luò)錯(cuò)誤時(shí)的邏輯,比如顯示一個(gè)錯(cuò)誤頁面

? ? ? ? ? ? ? *

? ? ? ? ? ? ? * 這里我偷個(gè)懶不寫了

? ? ? ? ? ? ? * */}@TargetApi(Build.VERSION_CODES.M)@OverridepublicvoidonReceivedHttpError(WebViewview,WebResourceRequestrequest,WebResourceResponseerrorResponse){super.onReceivedHttpError(view,request,errorResponse);}});}privatevoidinitWebChromeClient(){mWebView.setWebChromeClient(newWebChromeClient(){privateBitmapmDefaultVideoPoster;//默認(rèn)的視頻展示圖@OverridepublicvoidonReceivedTitle(WebViewview,Stringtitle){super.onReceivedTitle(view,title);setToolbarTitle(title);}@OverridepublicBitmapgetDefaultVideoPoster(){if(mDefaultVideoPoster==null){mDefaultVideoPoster=BitmapFactory.decodeResource(getResources(),R.drawable.video_default);returnmDefaultVideoPoster;}returnsuper.getDefaultVideoPoster();}});}/**

? * 設(shè)置Toolbar標(biāo)題

? *

? * @param title

? */privatevoidsetToolbarTitle(finalStringtitle){Log.d("setToolbarTitle"," WebDetailActivity "+title);if(mToolbar!=null){mToolbar.post(newRunnable(){@Overridepublicvoidrun(){mToolbar.setTitle(TextUtils.isEmpty(title)?getString(R.string.loading):title);}});}}@OverridepublicbooleanonCreateOptionsMenu(Menumenu){getMenuInflater().inflate(R.menu.menu_main,menu);returnsuper.onCreateOptionsMenu(menu);}@OverridepublicbooleanonOptionsItemSelected(MenuItemitem){switch(item.getItemId()){caseR.id.page_up:Toast.makeText(getApplicationContext(),"頁面向上",Toast.LENGTH_SHORT).show();mWebView.pageUp(true);break;caseR.id.page_down:Toast.makeText(getApplicationContext(),"頁面向下",Toast.LENGTH_SHORT).show();mWebView.pageDown(true);break;caseR.id.refresh:Toast.makeText(getApplicationContext(),"刷新~",Toast.LENGTH_SHORT).show();mWebView.reload();default:returnsuper.onOptionsItemSelected(item);}returnsuper.onOptionsItemSelected(item);}@OverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){//如果按下的是回退鍵且歷史記錄里確實(shí)還有頁面if((keyCode==KeyEvent.KEYCODE_BACK)&&mWebView.canGoBack()){mWebView.goBack();returntrue;}else{Toast.makeText(getApplicationContext(),"考試結(jié)束,恭喜您考試合格!",Toast.LENGTH_LONG).show();}returnsuper.onKeyDown(keyCode,event);}}

效果圖:

fuck,效果圖太大壓縮無望~~~效果圖看外鏈視頻吧

上路

好了,到此為止你已閱讀完所有的指南,送你一輛車,上路吧。

參考文章

Android官方文檔

Android Web Apps Using Android WebView

史上最全webview詳解

H5 緩存機(jī)制淺析 移動(dòng)端 Web 加載性能優(yōu)化

WebView加載速度優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 本文收集了我自己工作以來提交代碼前的所有檢查點(diǎn)。事實(shí)證明,這樣能有效提高自己的代碼質(zhì)量和功能的穩(wěn)定性。所以推...
    InKenKa閱讀 4,828評(píng)論 24 124
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,745評(píng)論 25 709
  • 河?xùn)|巨賈劉員外有一女,喚作素女。素女年十六,杏眼桃腮,云髻高鬟。每于閣中抬首遠(yuǎn)眺而云彩自來。 一日夜寢入寐。夢(mèng)...
    形聲閱讀 1,017評(píng)論 13 8
  • 經(jīng)常閱讀書籍附贈(zèng)代碼時(shí)或者其他源碼時(shí),會(huì)遇到很多GBK編碼的文本文件,比如java文件,其中的中文在其他編輯器下閱...
    Briarbear閱讀 2,491評(píng)論 0 0
  • (1) K線又稱為蠟燭線,記錄交易時(shí)間內(nèi)的開盤與收盤的漲落,并且將交易時(shí)間內(nèi)的最高價(jià)和最低價(jià)以上影線和下影線的形式...
    從小白到精通閱讀 680評(píng)論 0 0

友情鏈接更多精彩內(nèi)容