WebView 開車指南之最全實用案例

ips

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

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

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

前言

喝酒不開車,開車不喝酒锦溪。

目錄

WebView簡介

WebView基本使用

WebView常用方法

WebSettings

WebViewClient

WebChromeClient

JavaScript與WebView交互

WebView加載優(yōu)化

駕照考試

上路

WebView簡介

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

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

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

從Android

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

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

Migrating to WebView in Android 4.4

在WebView中易迹,有幾個地方是我們可以使用來定制我們的WebView各種行為的宰衙,分別是:WebSettingsJavaScriptInterface睹欲、WebViewClient以及WebChromeClient供炼。這些我都會在接下來的文章中一一介紹。

WebView基本使用

下面簡單介紹下WebView的基本使用:

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

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

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

接著在AndroidManifest聲明訪問網(wǎng)絡的權(quá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();}});

這時虫腋,運行app,里面就會多出一個Button~

但如果你真的運行的話稀余,你就會發(fā)現(xiàn)悦冀,app會自動跳到瀏覽器并打開指定的網(wǎng)頁,而并非在app內(nèi)展示網(wǎng)頁滚躯,那這就與我們的初衷背道而馳了雏门,那么要如何實現(xiàn)網(wǎng)頁在App內(nèi)打開呢?這就引出了下面的章節(jié)會提到的東西:WebViewClient嘿歌。我先將代碼貼出掸掏,具體實現(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簡介

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

String getUrl():獲取當前頁面的URL步脓。

reload():重新reload當前的URL愿待,即刷新。

boolean canGoBack():用來確認WebView里是否還有可回退的歷史記錄靴患。通常我們會在WebView里重寫返回鍵的點擊事件仍侥,通過該方法判斷WebView里是否還有歷史記錄,若有則返回上一頁鸳君。

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

boolean canGoBackOrForward(int steps):以當前的頁面為起始點,用來確認WebView的歷史記錄是否足以后退或前進給定的步數(shù)或颊,正數(shù)為前進砸紊,負數(shù)為后退。

goBack():在WebView歷史記錄后退到上一項囱挑。

goForward():在WebView歷史記錄里前進到下一項醉顽。

goBackOrForward(int steps):以當前頁面為起始點,前進或后退歷史記錄中指定的步數(shù)平挑,正數(shù)為前進游添,負數(shù)為后退。

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

clearHistory():清除當前webview訪問的歷史記錄隐解。

clearFormData():清除自動完成填充的表單數(shù)據(jù)鞍帝。需要注意的是,該方法僅僅清除當前表單域自動完成填充的表單數(shù)據(jù)煞茫,并不會清除WebView存儲到本地的數(shù)據(jù)帕涌。

onPause():當頁面被失去焦點被切換到后臺不可見狀態(tài),需要執(zhí)行onPause操作续徽,該操作會通知內(nèi)核安全地暫停所有動作蚓曼,比如動畫的執(zhí)行或定位的獲取等。需要注意的是該方法并不會暫停JavaScript的執(zhí)行钦扭,若要暫停JavaScript的執(zhí)行請使用接下來的這個方法纫版。

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

pauseTimers():該方法面向全局整個應用程序的webview其弊,它會暫停所有webview的layout,parsing膀斋,JavaScript Timer梭伐。當程序進入后臺時,該方法的調(diào)用可以降低CPU功耗仰担。

resumeTimers():恢復pauseTimers時的所有操作糊识。

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

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

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

getHeight():方法都返回當前WebView這個容器的高度项鬼。其實以上兩個方法都屬于View哑梳。

getContentHeight():該方法返回整個HTML頁面的高度,但該高度值并不等同于當前整個頁面的高度绘盟,因為WebView有縮放功能鸠真, 所以當前整個頁面的高度實際上應該是原始HTML的高度再乘上縮放比例。因此龄毡,準確的判斷方法應該是

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

pageUp(boolean top):將WebView展示的頁面滑動至頂部吠卷。

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

WebSettings

WebSettings是用來管理WebView配置的類沦零。當WebView第一次創(chuàng)建時祭隔,內(nèi)部會包含一個默認配置的集合。若我們想更改這些配置路操,便可以通過WebSettings里的方法來進行設置疾渴。

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

獲取WebSettings對象

WebSettingswebSettings=webView.getSettings();

WebSettings常用方法

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

setJavaScriptEnabled(boolean flag):設置WebView是否可以運行JavaScript。

setJavaScriptCanOpenWindowsAutomatically(boolean flag):設置WebView是否可以由JavaScript自動打開窗口店量,默認為false芜果,通常與JavaScript的window.open()配合使用。

setAllowFileAccess(boolean allow):啟用或禁用WebView訪問文件數(shù)據(jù)垫桂。

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

這個時候疼鸟,需要確保這個app擁有訪問Internet的權(quán)限,否則會拋出安全異常庙曙。

通常沒有禁止圖片加載的需求的時候空镜,完全不用管這個方法,因為當我們的app擁有訪問Internet的權(quán)限時捌朴,這個flag的默認值就是false吴攒。

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

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

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

setLayoutAlgorithm(WebSettings.LayoutAlgorithm l):指定WebView的頁面布局顯示形式,調(diào)用該方法會引起頁面重繪左驾。默認值為LayoutAlgorithm#NARROW_COLUMNS镣隶。

setNeedInitialFocus(boolean flag):通知WebView是否需要設置一個節(jié)點獲取焦點當WebView#requestFocus(int,android.graphics.Rect)被調(diào)用時,默認為true诡右。

setAppCacheEnabled(boolean flag):啟用或禁用應用緩存安岂。

setAppCachePath(String appCachePath):設置應用緩存路徑,這個路徑必須是可以讓app寫入文件的帆吻。該方法應該只被調(diào)用一次域那,重復調(diào)用會被無視~

setCacheMode(int mode)用來設置WebView的緩存模式。當我們加載頁面或從上一個頁面返回的時候猜煮,會按照設置的緩存模式去檢查并使用(或不使用)緩存次员。

緩存模式有四種:

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

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

LOAD_NO_CACHE:不加載緩存绪妹,只從網(wǎng)絡加載數(shù)據(jù)甥桂。

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

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

setDatabaseEnabled(boolean flag):啟用或禁用數(shù)據(jù)庫緩存律歼。

setDomStorageEnabled(boolean flag):啟用或禁用DOM緩存民镜。

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

setDefaultEncodingName(String encoding):設置編碼格式险毁,通常都設為“UTF-8”制圈。

setStandardFontFamily(String font):設置標準的字體族,默認“sans-serif”畔况。

setCursiveFontFamily:設置草書字體族鲸鹦,默認“cursive”。

setFantasyFontFamily:設置CursiveFont字體族跷跪,默認“cursive”馋嗜。

setFixedFontFamily:設置混合字體族,默認“monospace”域庇。

setSansSerifFontFamily:設置梵文字體族嵌戈,默認“sans-serif”。

setSerifFontFamily:設置襯線字體族听皿,默認“sans-serif”

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

setDefaultFontSize(int size):設置默認字體大小,默認16九府,取值區(qū)間[1-72]椎瘟,超過范圍,使用其上限值侄旬。

setMinimumFontSize:設置最小字體肺蔚,默認8. 取值區(qū)間[1-72],超過范圍儡羔,使用其上限值宣羊。

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

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

WebViewClient

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

onLoadResource(WebView view, String url):該方法在加載頁面資源時會回調(diào)莱预,每一個資源(比如圖片)的加載都會調(diào)用一次柠掂。

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

onPageFinished(WebView view, String url):該方法只在WebView完成一個頁面加載時調(diào)用一次(同樣也只在Main frame loading時調(diào)用)危喉,我們可以可以在此時關閉加載動畫,進行其他操作州疾。

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

1.這個方法只在與服務器無法正常連接時調(diào)用薄嫡,類似于服務器返回錯誤碼的那種錯誤(即HTTP ERROR),該方法是不會回調(diào)的颗胡,因為你已經(jīng)和服務器正常連接上了(全怪官方文檔(︶^︶))毫深;

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

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

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

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request):當WebView需要請求某個數(shù)據(jù)時窖维,這個方法可以攔截該請求來告知app并且允許app本身返回一個數(shù)據(jù)來替代我們原本要加載的數(shù)據(jù)榆综。

比如你對web的某個js做了本地緩存,希望在加載該js時不再去請求服務器而是可以直接讀取本地緩存的js铸史,這個方法就可以幫助你完成這個需求鼻疮。你可以寫一些邏輯檢測這個request,并返回相應的數(shù)據(jù)琳轿,你返回的數(shù)據(jù)就會被WebView使用判沟,如果你返回null讯屈,WebView會繼續(xù)向服務器請求爽撒。

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

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

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

WebChromeClient

如果說WebViewClient是幫助WebView處理各種通知闹伪、請求事件的“內(nèi)政大臣”的話沪铭,那么WebChromeClient就是輔助WebView處理Javascript的對話框,網(wǎng)站圖標偏瓤,網(wǎng)站title杀怠,加載進度等偏外部事件的“外交大臣”。

onProgressChanged(WebView view, int newProgress):當頁面加載的進度發(fā)生改變時回調(diào)厅克,用來告知主程序當前頁面的加載進度赔退。

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

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

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

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

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

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

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

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

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

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

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

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

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

onPermissionRequestCanceled(PermissionRequest request):該方法在web權(quán)限申請權(quán)限被取消時回調(diào),這時應該隱藏任何與之相關的UI界面而咆。

Js與WebView交互

既然嗨鳥應用大行其道霍比,那么毫無疑問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對象注解的方式來建立起Javascript對象和Android原生對象的綁定,提供給JavScript調(diào)用的函數(shù)必須帶有@JavascriptInterface涯捻。

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

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

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

2.注冊JavaScriptInterface

webView.addJavascriptInterface(this,"android");

addJavascriptInterface的作用是把this所代表的類映射為JavaScript中的android對象浅妆。

3.編寫JavaScript代碼

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

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

1.定義一個帶返回值的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交互的一些介紹障癌,希望能對你有幫助凌外。

WebView加載優(yōu)化

當WebView的使用頻率變得頻繁的時候,對于其各方面的優(yōu)化就變得逐漸重要了起來涛浙】导可以知道的是,我們每加載一個

H5頁面蝗拿,都會有很多的請求晾捏。除了HTML主URL自身的請求外,HTML外部引用的 JS哀托、CSS惦辛、字體文件、圖片都是一個個獨立的HTTP

請求仓手,雖然請求是并發(fā)的胖齐,但當網(wǎng)頁整體數(shù)量達到一定程度的時候,再加上瀏覽器解析嗽冒、渲染的時間呀伙,Web整體的加載時間變得很長。同時請求文件越多添坊,消耗的流量也會越多剿另。那么對于加載的優(yōu)化就變得非常重要,這方面的經(jīng)驗我也沒有什么別的贬蛙,大概三個方面:

一個雨女,就是資源本地化的問題

首先可以明確的是,以目前的網(wǎng)絡條件阳准,通過網(wǎng)絡去服務器獲取資源的速度是遠遠比不上從本地讀取的氛堕。談論各種優(yōu)化策略其實恰恰忽略了“需要加載”才是阻擋速度提升的最大絆腳石。所以我們的思路一野蝇,就是將一些較重的資源比如js讼稚、css括儒、圖片甚至HTML本身進行本地化處理,在每次加載到這些資源的時候锐想,從本地讀取進行加載帮寻,可以簡單記憶為“存·取·更”

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

“存”——將上述重量級資源打包進apk文件痛倚,每次加載相應文件時時從本地取即可规婆。也可不打包,在第一次加載時以及接下來的若干間隔時間里動態(tài)下載存儲蝉稳,將所有的資源文件都存在Android的asset目錄下抒蚜;

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

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

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

第二個撞秋,就是緩存的問題

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

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

在網(wǎng)絡正常時吻贿,采用默認緩存策略串结,在緩存可獲取并且沒有過期的情況下加載緩存,否則通過網(wǎng)絡獲取資源以減少頁面的網(wǎng)絡請求次數(shù)舅列。

這里值得提起的是肌割,我們經(jīng)常在app里用WebView展示頁面時,并不想讓用戶覺得他是在訪問一個網(wǎng)頁帐要。因為倘若我們的app里網(wǎng)頁非常多把敞,而我們給用戶的感覺又都像在訪問網(wǎng)頁的話,我們的app便失去了意義榨惠。(我的意思是為什么用戶不直接使用瀏覽器呢奋早?)

所以這時,離線緩存的問題就值得我們注意赠橙。我們需要讓用戶在沒有網(wǎng)的時候伸蚯,依然能夠操作我們的app,而不是面對一個和瀏覽器里的網(wǎng)絡錯誤一樣的頁面简烤,哪怕他能進行的操作十分有限。

這里我的思路是摇幻,在開啟緩存的前提下横侦,WebView在加載頁面時檢測網(wǎng)絡變化挥萌,倘若在加載頁面時用戶的網(wǎng)絡突然斷掉,我們應當更改WebView的緩存策略枉侧。

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

既然有緩存引瀑,就要有緩存控制,與一相似的是我們也要建立緩存控制機制榨馁,定期或接受服務器通知來進行緩存的清空或更新憨栽。

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

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

{})后才會渲染并顯示頁面。這是不可接受的招拙,所以我們需要對Js進行延遲加載唧瘾,當然這部分是Web前端的工作。

如果說還有什么

那就是JsBridge一律不得濫用别凤,這個對頁面加載的完成速度是有很大影響的饰序,倘若一個頁面很多操作都通過JSbridge來控制,再怎么優(yōu)化也無濟于事(因為畢竟有那么多操作要實際執(zhí)行)规哪。同時要注意的是求豫,不管你是否對資源進行緩存,都請將資源在服務器端進行壓縮由缆。因為無論是資源的獲取和更新注祖,都是要從服務器獲取的,所以對于資源文件的壓縮其實是最直接也最應該做的事情之一均唉,但是一般服務器端都會做好是晨,所以主要就是上面這三件事。

駕照考試

介紹了這么多舔箭,希望能對你有點幫助罩缴。接下來時純實戰(zhàn)時間,我會將上面所介紹的很多知識點在接下來的代碼里實際應用一遍层扶,希望能夠帶給你更加直觀的使用感受箫章。

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();//支持獲取手勢焦點mWebView.requestFocusFromTouch();//支持JSsettings.setJavaScriptEnabled(true);//支持插件settings.setPluginState(WebSettings.PluginState.ON);//設置適應屏幕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);//設置緩存模式settings.setDomStorageEnabled(true);settings.setDatabaseEnabled(true);settings.setCacheMode(WebSettings.LOAD_DEFAULT);settings.setAppCacheEnabled(true);settings.setAppCachePath(mWebView.getContext().getCacheDir().getAbsolutePath());//設置可訪問文件settings.setAllowFileAccess(true);//當webview調(diào)用requestFocus時為webview設置節(jié)點settings.setNeedInitialFocus(true);//支持自動加載圖片if(Build.VERSION.SDK_INT>=19){settings.setLoadsImagesAutomatically(true);}else{settings.setLoadsImagesAutomatically(false);}settings.setNeedInitialFocus(true);//設置編碼格式settings.setDefaultTextEncodingName("UTF-8");}privatevoidinitWebViewClient(){mWebView.setWebViewClient(newWebViewClient(){//頁面開始加載時@OverridepublicvoidonPageStarted(WebViewview,Stringurl,Bitmapfavicon){super.onPageStarted(view,url,favicon);mProgressbar.setVisibility(View.VISIBLE);}//頁面完成加載時@OverridepublicvoidonPageFinished(WebViewview,Stringurl){super.onPageFinished(view,url);mProgressbar.setVisibility(View.GONE);}//是否在WebView內(nèi)加載新頁面@OverridepublicbooleanshouldOverrideUrlLoading(WebViewview,WebResourceRequestrequest){view.loadUrl(request.toString());returntrue;}//網(wǎng)絡錯誤時回調(diào)的方法@OverridepublicvoidonReceivedError(WebViewview,WebResourceRequestrequest,WebResourceErrorerror){super.onReceivedError(view,request,error);/**

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

? ? ? ? ? ? ? *

? ? ? ? ? ? ? * 這里我偷個懶不寫了

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

? * 設置Toolbar標題

? *

? * @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){//如果按下的是回退鍵且歷史記錄里確實還有頁面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 緩存機制淺析 移動端 Web 加載性能優(yōu)化

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桶至,一起剝皮案震驚了整個濱河市昼伴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镣屹,老刑警劉巖圃郊,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異女蜈,居然都是意外死亡持舆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門伪窖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸寓,“玉大人,你說我怎么就攤上這事惰许∠玻” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵汹买,是天一觀的道長佩伤。 經(jīng)常有香客問我,道長晦毙,這世上最難降的妖魔是什么生巡? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮见妒,結(jié)果婚禮上孤荣,老公的妹妹穿的比我還像新娘。我一直安慰自己须揣,他們只是感情好盐股,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耻卡,像睡著了一般疯汁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卵酪,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天幌蚊,我揣著相機與錄音,去河邊找鬼溃卡。 笑死溢豆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瘸羡。 我是一名探鬼主播漩仙,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了讯赏?” 一聲冷哼從身側(cè)響起垮兑,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漱挎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀哨,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡磕谅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雾棺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膊夹。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捌浩,靈堂內(nèi)的尸體忽然破棺而出放刨,到底是詐尸還是另有隱情,我是刑警寧澤尸饺,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布进统,位于F島的核電站,受9級特大地震影響浪听,放射性物質(zhì)發(fā)生泄漏螟碎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一迹栓、第九天 我趴在偏房一處隱蔽的房頂上張望掉分。 院中可真熱鬧,春花似錦克伊、人聲如沸酥郭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽不从。三九已至,卻和暖如春洗搂,著一層夾襖步出監(jiān)牢的瞬間消返,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工耘拇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撵颊,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓惫叛,卻偏偏與公主長得像倡勇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嘉涌,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 前言 本文收集了我自己工作以來提交代碼前的所有檢查點妻熊。事實證明夸浅,這樣能有效提高自己的代碼質(zhì)量和功能的穩(wěn)定性。所以推...
    InKenKa閱讀 4,669評論 24 124
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,522評論 25 707
  • 河東巨賈劉員外有一女扔役,喚作素女帆喇。素女年十六,杏眼桃腮亿胸,云髻高鬟坯钦。每于閣中抬首遠眺而云彩自來。 一日夜寢入寐侈玄。夢...
    形聲閱讀 873評論 13 8
  • 經(jīng)常閱讀書籍附贈代碼時或者其他源碼時婉刀,會遇到很多GBK編碼的文本文件,比如java文件序仙,其中的中文在其他編輯器下閱...
    Briarbear閱讀 2,378評論 0 0
  • (1) K線又稱為蠟燭線突颊,記錄交易時間內(nèi)的開盤與收盤的漲落,并且將交易時間內(nèi)的最高價和最低價以上影線和下影線的形式...
    從小白到精通閱讀 543評論 0 0