WebView·開車指南
目錄
- 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應(yīng)用似乎占據(jù)的APP的主流類型彪见,那么關(guān)于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各種行為的,分別是:WebSettings锅知、JavaScriptInterface播急、WebViewClient以及WebChromeClient。這些我都會在接下來的文章中一一介紹售睹。
WebView基本使用
下面簡單介紹下WebView的基本使用:首先新建一個工程桩警,在layout文件里放入一個WebView控件(當(dāng)然也可以通過Java代碼動態(tài)放入,這里不演示了)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
然后在Activity的onCreate方法里寫入如下代碼:
String url = "https://www.google.com";
WebView webView = (WebView) findViewById(R.id.web_view);
webView.loadUrl(url);
接著在AndroidManifest聲明訪問網(wǎng)絡(luò)的權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
就昌妹,完事了~這時運行app捶枢,它已經(jīng)可以訪問指定地址的網(wǎng)頁了。上面提到了WebView繼承自AbsoluteLayout飞崖,可以在其中放入一些子View烂叔,那也順手來一下。Layout文件改為:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="170dp"
android:layout_y="400dp"
android:background="@color/colorAccent"
android:text="@string/app_name" />
</WebView>
</LinearLayout>
Activity的onCreate里加上:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
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代碼(最終)如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String url = "https://www.google.com";
WebView webView = (WebView) findViewById(R.id.web_view);
webView.loadUrl(url);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.toString());
return true;
} }); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "系好安全帶!", Toast.LENGTH_SHORT).show(); } }); }}
WebView簡介
接下來再介紹一些WebView的常用方法,具體演示會在后面章節(jié)的代碼里統(tǒng)一展示脊另。
String getUrl():獲取當(dāng)前頁面的URL导狡。
reload():重新reload當(dāng)前的URL,即刷新偎痛。
boolean canGoBack():用來確認WebView里是否還有可回退的歷史記錄旱捧。通常我們會在WebView里重寫返回鍵的點擊事件,通過該方法判斷WebView里是否還有歷史記錄踩麦,若有則返回上一頁枚赡。
boolean canGoForward():用來確認WebView是否還有可向前的歷史記錄。
boolean canGoBackOrForward(int steps):以當(dāng)前的頁面為起始點谓谦,用來確認WebView的歷史記錄是否足以后退或前進給定的步數(shù)贫橙,正數(shù)為前進,負數(shù)為后退反粥。
goBack():在WebView歷史記錄后退到上一項卢肃。
goForward():在WebView歷史記錄里前進到下一項疲迂。
goBackOrForward(int steps):以當(dāng)前頁面為起始點,前進或后退歷史記錄中指定的步數(shù)莫湘,正數(shù)為前進尤蒿,負數(shù)為后退。
clearCache(boolean includeDiskFiles):清空網(wǎng)頁訪問留下的緩存數(shù)據(jù)幅垮。需要注意的時腰池,由于緩存是全局的,所以只要是WebView用到的緩存都會被清空忙芒,即便其他地方也會使用到示弓。該方法接受一個參數(shù),從命名即可看出作用呵萨。若設(shè)為false奏属,則只清空內(nèi)存里的資源緩存,而不清空磁盤里的甘桑。
clearHistory():清除當(dāng)前webview訪問的歷史記錄拍皮。
clearFormData():清除自動完成填充的表單數(shù)據(jù)。需要注意的是跑杭,該方法僅僅清除當(dāng)前表單域自動完成填充的表單數(shù)據(jù)铆帽,并不會清除WebView存儲到本地的數(shù)據(jù)。
onPause():當(dāng)頁面被失去焦點被切換到后臺不可見狀態(tài)德谅,需要執(zhí)行onPause操作爹橱,該操作會通知內(nèi)核安全地暫停所有動作,比如動畫的執(zhí)行或定位的獲取等窄做。需要注意的是該方法并不會暫停JavaScript的執(zhí)行愧驱,若要暫停JavaScript的執(zhí)行請使用接下來的這個方法。
onResume():在先前調(diào)用onPause()后椭盏,我們可以調(diào)用該方法來恢復(fù)WebView的運行组砚。
pauseTimers():該方法面向全局整個應(yīng)用程序的webview,它會暫停所有webview的layout掏颊,parsing糟红,JavaScript Timer。當(dāng)程序進入后臺時乌叶,該方法的調(diào)用可以降低CPU功耗盆偿。resumeTimers():恢復(fù)pauseTimers時的所有操作。
destroy():銷毀WebView准浴。需要注意的是:這個方法的調(diào)用應(yīng)在WebView從父容器中被remove掉之后事扭。我們可以手動地調(diào)用
rootLayout.removeView(webView);webView.destroy();
getScrollY():該方法返回的當(dāng)前可見區(qū)域的頂端距整個頁面頂端的距離,也就是當(dāng)前內(nèi)容滾動的距離乐横。
getHeight():方法都返回當(dāng)前WebView這個容器的高度求橄。其實以上兩個方法都屬于View今野。
getContentHeight():該方法返回整個HTML頁面的高度,但該高度值并不等同于當(dāng)前整個頁面的高度罐农,因為WebView有縮放功能腥泥, 所以當(dāng)前整個頁面的高度實際上應(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展示的頁面滑動至頂部。
pageDown(boolean bottom):將WebView展示的頁面滑動至底部蛆楞。
WebSettings
WebSettings是用來管理WebView配置的類溯乒。當(dāng)WebView第一次創(chuàng)建時,內(nèi)部會包含一個默認配置的集合豹爹。若我們想更改這些配置裆悄,便可以通過WebSettings里的方法來進行設(shè)置。
WebSettings對象可以通過WebView.getSettings()獲得臂聋,它的生命周期是與它的WebView本身息息相關(guān)的光稼,如果WebView被銷毀了,那么任何由WebSettings調(diào)用的方法也同樣不能使用孩等。
獲取WebSettings對象
WebSettings webSettings = webView.getSettings();
WebSettings常用方法
(幾乎所有的set方法都有相應(yīng)的get方法艾君,這里就只介紹set了。另肄方,所有未寫方法返回值類型的皆為空類型)
- setJavaScriptEnabled(boolean flag):設(shè)置WebView是否可以運行JavaScript冰垄。
- setJavaScriptCanOpenWindowsAutomatically(boolean flag):設(shè)置WebView是否可以由JavaScript自動打開窗口,默認為false权她,通常與JavaScript的window.open()配合使用虹茶。
- setAllowFileAccess(boolean allow):啟用或禁用WebView訪問文件數(shù)據(jù)。
- setBlockNetworkImage(boolean flag):禁止或允許WebView從網(wǎng)絡(luò)上加載圖片隅要。需要注意的是蝴罪,如果設(shè)置是從禁止到允許的轉(zhuǎn)變的話,圖片數(shù)據(jù)并不會在設(shè)置改變后立刻去獲取步清,而是在WebView調(diào)用reload()的時候才會生效要门。這個時候,需要確保這個app擁有訪問Internet的權(quán)限尼啡,否則會拋出安全異常暂衡。通常沒有禁止圖片加載的需求的時候,完全不用管這個方法崖瞭,因為當(dāng)我們的app擁有訪問Internet的權(quán)限時狂巢,這個flag的默認值就是false。
- setSupportZoom(boolean support):設(shè)置是否支持縮放书聚。
- setBuiltInZoomControls(boolean enabled):顯示或不顯示縮放按鈕(wap網(wǎng)頁不支持)唧领。
- setSupportMultipleWindows(boolean support):設(shè)置WebView是否支持多窗口藻雌。
- setLayoutAlgorithm(WebSettings.LayoutAlgorithm l):指定WebView的頁面布局顯示形式,調(diào)用該方法會引起頁面重繪斩个。默認值為LayoutAlgorithm#NARROW_COLUMNS胯杭。
- setNeedInitialFocus(boolean flag):通知WebView是否需要設(shè)置一個節(jié)點獲取焦點當(dāng)WebView#requestFocus(int,android.graphics.Rect)被調(diào)用時,默認為true做个。
- setAppCacheEnabled(boolean flag):啟用或禁用應(yīng)用緩存。
- setAppCachePath(String appCachePath):設(shè)置應(yīng)用緩存路徑居暖,這個路徑必須是可以讓app寫入文件的藤肢。該方法應(yīng)該只被調(diào)用一次太闺,重復(fù)調(diào)用會被無視~
- setCacheMode(int mode):用來設(shè)置WebView的緩存模式。當(dāng)我們加載頁面或從上一個頁面返回的時候嘁圈,會按照設(shè)置的緩存模式去檢查并使用(或不使用)緩存省骂。
緩存模式有四種:
LOAD_DEFAULT:默認的緩存使用模式最住。在進行頁面前進或后退的操作時,如果緩存可用并未過期就優(yōu)先加載緩存涨缚,否則從網(wǎng)絡(luò)上加載數(shù)據(jù)略贮。這樣可以減少頁面的網(wǎng)絡(luò)請求次數(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)的時候使用LOAD_DEFAULT载矿,離線時使用LOAD_CACHE_ONLY、LOAD_CACHE_ELSE_NETWORK闷盔,讓用戶不至于在離線時啥都看不到消约。setDatabaseEnabled(boolean flag):啟用或禁用數(shù)據(jù)庫緩存胜嗓。
setDomStorageEnabled(boolean flag):啟用或禁用DOM緩存。
setUserAgentString(String ua):設(shè)置WebView的UserAgent值逃贝。
setDefaultEncodingName(String encoding):設(shè)置編碼格式迫摔,通常都設(shè)為“UTF-8”秋泳。
setStandardFontFamily(String font):設(shè)置標(biāo)準(zhǔn)的字體族攒菠,默認“sans-serif”歉闰。
setCursiveFontFamily:設(shè)置草書字體族,默認“cursive”和敬。
setFantasyFontFamily:設(shè)置CursiveFont字體族,默認“cursive”啤它。
setFixedFontFamily:設(shè)置混合字體族舱痘,默認“monospace”。
setSansSerifFontFamily:設(shè)置梵文字體族塌碌,默認“sans-serif”。
setSerifFontFamily:設(shè)置襯線字體族台妆,默認“sans-serif”
setDefaultFixedFontSize(int size):設(shè)置默認填充字體大小胖翰,默認16,取值區(qū)間為[1-72]萨咳,超過范圍,使用其上限值培他。
setDefaultFontSize(int size):設(shè)置默認字體大小而克,默認16怔毛,取值區(qū)間[1-72],超過范圍拣度,使用其上限值。
setMinimumFontSize:設(shè)置最小字體抗果,默認8. 取值區(qū)間[1-72],超過范圍日麸,使用其上限值逮光。
setMinimumLogicalFontSize:設(shè)置最小邏輯字體代箭,默認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)該方法一次。我們可以在這個方法里設(shè)定開啟一個加載的動畫与境,告訴用戶程序在等待網(wǎng)絡(luò)的響應(yīng)猖吴。
onPageFinished(WebView view, String url):該方法只在WebView完成一個頁面加載時調(diào)用一次(同樣也只在Main frame loading時調(diào)用),我們可以可以在此時關(guān)閉加載動畫海蔽,進行其他操作。
onReceivedError(WebView view, WebResourceRequest request, WebResourceError error):該方法在web頁面加載錯誤時回調(diào)拗引,這些錯誤通常都是由無法與服務(wù)器正常連接引起的,最常見的就是網(wǎng)絡(luò)問題矾削。 這個方法有兩個地方需要注意:
1.這個方法只在與服務(wù)器無法正常連接時調(diào)用豁护,類似于服務(wù)器返回錯誤碼的那種錯誤(即HTTP ERROR),該方法是不會回調(diào)的择镇,因為你已經(jīng)和服務(wù)器正常連接上了(全怪官方文檔(︶^︶))括改;
2.這個方法是新版本的onReceivedError()方法,從API23開始引進嘱能,與舊方法onReceivedError(WebView view,int errorCode,String description,String failingUrl)不同的是,新方法在頁面局部加載發(fā)生錯誤時也會被調(diào)用(比如頁面里兩個子Tab或者一張圖片)苏携。這就意味著該方法的調(diào)用頻率可能會更加頻繁对粪,所以我們應(yīng)該在該方法里執(zhí)行盡量少的操作。
onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse):上一個方法提到onReceivedError并不會在服務(wù)器返回錯誤碼時被回調(diào)著拭,那么當(dāng)我們需要捕捉HTTP ERROR并進行相應(yīng)操作時應(yīng)該怎么辦呢?API23便引入了該方法乳蛾。當(dāng)服務(wù)器返回一個HTTP ERROR并且它的status code>=400時,該方法便會回調(diào)肃叶。這個方法的作用域并不局限于Main Frame,任何資源的加載引發(fā)HTTP ERROR都會引起該方法的回調(diào)因惭,所以我們也應(yīng)該在該方法里執(zhí)行盡量少的操作,只進行非常必要的錯誤處理等浸锨。
onReceivedSslError(WebView view, SslErrorHandler handler, SslError error):當(dāng)WebView加載某個資源引發(fā)SSL錯誤時會回調(diào)該方法,這時WebView要么執(zhí)行handler.cancel()取消加載柱搜,要么執(zhí)行handler.proceed()方法繼續(xù)加載(默認為cancel)剥险。需要注意的是,這個決定可能會被保留并在將來再次遇到SSL錯誤時執(zhí)行同樣的操作表制。
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request):當(dāng)WebView需要請求某個數(shù)據(jù)時,這個方法可以攔截該請求來告知app并且允許app本身返回一個數(shù)據(jù)來替代我們原本要加載的數(shù)據(jù)娜遵。
比如你對web的某個js做了本地緩存壤短,希望在加載該js時不再去請求服務(wù)器而是可以直接讀取本地緩存的js设拟,這個方法就可以幫助你完成這個需求久脯。你可以寫一些邏輯檢測這個request,并返回相應(yīng)的數(shù)據(jù)跑慕,你返回的數(shù)據(jù)就會被WebView使用摧找,如果你返回null核行,WebView會繼續(xù)向服務(wù)器請求蹬耘。
boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request):哈~ 終于到了這個方法,在最開始的基礎(chǔ)演示時我們用到了這個方法绵脯。從實踐中我們知道,當(dāng)我們沒有給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):當(dāng)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)站圖標(biāo),網(wǎng)站title献丑,加載進度等偏外部事件的“外交大臣”光督。
onProgressChanged(WebView view, int newProgress):當(dāng)頁面加載的進度發(fā)生改變時回調(diào)塔粒,用來告知主程序當(dāng)前頁面的加載進度。
onReceivedIcon(WebView view, Bitmap icon):用來接收web頁面的icon卒茬,我們可以在這里將該頁面的icon設(shè)置到Toolbar。
onReceivedTitle(WebView view, String title):用來接收web頁面的title柳畔,我們可以在這里將頁面的title設(shè)置到Toolbar郭赐。
以下兩個方法是為了支持web頁面進入全屏模式而存在的(比如播放視頻)薪韩,如果不實現(xiàn)這兩個方法,該web上的內(nèi)容便不能進入全屏模式罗捎。
onShowCustomView(View view, WebChromeClient.CustomViewCallback callback):該方法在當(dāng)前頁面進入全屏模式時回調(diào)拉盾,主程序必須提供一個包含當(dāng)前web內(nèi)容(視頻 or Something)的自定義的View。
onHideCustomView():該方法在當(dāng)前頁面退出全屏模式時回調(diào)捉偏,主程序應(yīng)在這時隱藏之前show出來的View。
Bitmap getDefaultVideoPoster():當(dāng)我們的Web頁面包含視頻時霞掺,我們可以在HTML里為它設(shè)置一個預(yù)覽圖驻粟,WebView會在繪制頁面時根據(jù)它的寬高為它布局根悼。而當(dāng)我們處于弱網(wǎng)狀態(tài)下時蜀撑,我們沒有比較快的獲取該圖片,那WebView繪制頁面時的gitWidth()方法就會報出空指針異常~ 于是app就crash了矿卑。沃饶。
這時我們就需要重寫該方法母廷,在我們尚未獲取web頁面上的video預(yù)覽圖時糊肤,給予它一個本地的圖片,避免空指針的發(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)沥割。我們應(yīng)該在這里打開一個文件選擇器,如果要取消這個請求我們可以調(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)枉阵,這時應(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代碼
function readyToGo() { alert("Hello") } function alertMessage(message) { alert(message) } function getYourCar(){ return "Car"; }
1. WebView調(diào)用JavaScript無參無返回值函數(shù)
String call = "javascript:readyToGo()";webView.loadUrl(call);
2. WebView調(diào)用JavScript有參無返回值函數(shù)
String call = "javascript:alertMessage(\"" + "content" + "\")";webView.loadUrl(call);
3. WebView調(diào)用JavaScript有參數(shù)有返回值的函數(shù)
@TargetApi(Build.VERSION_CODES.KITKAT)private void evaluateJavaScript(WebView webView){webView.evaluateJavascript("getYourCar()", new ValueCallback<String>() { @Override public void onReceiveValue(String s) { 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注解
@JavascriptInterface
public void show(String s){ Toast.makeText(getApplication(), s, Toast.LENGTH_SHORT).show();}
2.注冊JavaScriptInterface
webView.addJavascriptInterface(this, "android");
addJavascriptInterface的作用是把this所代表的類映射為JavaScript中的android對象忘古。
3.編寫JavaScript代碼
function toastClick(){ window.android.show("JavaScript called~!"); }
演示二 JavaScript調(diào)用有返回值的Java方法
1.定義一個帶返回值的Java方法,并使用@JavaInterface:
@JavaInterfacepublic String getMessage(){ return "Hello,boy~";}
2.添加JavaScript的映射
webView.addJavaScriptInterface(this,"Android");
3.通過JavaScript調(diào)用Java方法
function showHello(){ var str=window.Android.getMessage(); console.log(str);}
以上就是Js與WebView交互的一些介紹髓堪,希望能對你有幫助。
WebView加載優(yōu)化
當(dāng)WebView的使用頻率變得頻繁的時候骤菠,對于其各方面的優(yōu)化就變得逐漸重要了起來它改“淘校可以知道的是央拖,我們每加載一個 H5頁面鹉戚,都會有很多的請求专控。除了HTML主URL自身的請求外,HTML外部引用的 JS赢底、CSS、字體文件幸冻、圖片都是一個個獨立的HTTP 請求咳焚,雖然請求是并發(fā)的,但當(dāng)網(wǎng)頁整體數(shù)量達到一定程度的時候革半,再加上瀏覽器解析、渲染的時間延刘,Web整體的加載時間變得很長六敬。同時請求文件越多,消耗的流量也會越多觉阅。那么對于加載的優(yōu)化就變得非常重要,這方面的經(jīng)驗我也沒有什么別的典勇,大概三個方面:
一個,就是資源本地化的問題
首先可以明確的是权烧,以目前的網(wǎng)絡(luò)條件伤溉,通過網(wǎng)絡(luò)去服務(wù)器獲取資源的速度是遠遠比不上從本地讀取的。談?wù)摳鞣N優(yōu)化策略其實恰恰忽略了“需要加載”才是阻擋速度提升的最大絆腳石乱顾。所以我們的思路一,就是將一些較重的資源比如js券时、css、圖片甚至HTML本身進行本地化處理橘洞,在每次加載到這些資源的時候,從本地讀取進行加載炸枣,可以簡單記憶為“存·取·更”。
具體實現(xiàn)思路為:
“存”——將上述重量級資源打包進apk文件博其,每次加載相應(yīng)文件時時從本地取即可迂猴。也可不打包,在第一次加載時以及接下來的若干間隔時間里動態(tài)下載存儲沸毁,將所有的資源文件都存在Android的asset目錄下;
“取”——重寫WebViewClient的WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)方法携兵,通過一定的判別方法(例如正則表達式)攔截相應(yīng)的請求搂誉,從本地讀取相應(yīng)資源并返回;
“更”——建立起Cache Control機制炭懊,定期或使用API通知的形式控制本地資源的更新,保證本地資源是最新和可用的嘲碧。這里附上一篇博客鏈接,非常棒可供參考:caching-web-resources-in-the-android-device
第二個愈涩,就是緩存的問題
倘若你不采用或不完全采用第一條資源本地化的思路加矛,那么你的WebView緩存是必須要開啟的(雖然這一思路和第一條有重合的地方)。
WebSettings settings = webView.getSettings();
settings.setAppCacheEnabled(true);
settings.setDatabaseEnabled(true);
settings.setDomStorageEnabled(true);//開啟DOM緩存settings.setCacheMode(WebSettings.LOAD_DEFAULT);
在網(wǎng)絡(luò)正常時斟览,采用默認緩存策略,在緩存可獲取并且沒有過期的情況下加載緩存狸棍,否則通過網(wǎng)絡(luò)獲取資源以減少頁面的網(wǎng)絡(luò)請求次數(shù)味悄。
這里值得提起的是,我們經(jīng)常在app里用WebView展示頁面時侍瑟,并不想讓用戶覺得他是在訪問一個網(wǎng)頁。因為倘若我們的app里網(wǎng)頁非常多费韭,而我們給用戶的感覺又都像在訪問網(wǎng)頁的話庭瑰,我們的app便失去了意義。(我的意思是為什么用戶不直接使用瀏覽器呢弹灭?)
所以這時,離線緩存的問題就值得我們注意穷吮。我們需要讓用戶在沒有網(wǎng)的時候,依然能夠操作我們的app八回,而不是面對一個和瀏覽器里的網(wǎng)絡(luò)錯誤一樣的頁面驾诈,哪怕他能進行的操作十分有限。
這里我的思路是乍迄,在開啟緩存的前提下,WebView在加載頁面時檢測網(wǎng)絡(luò)變化汉匙,倘若在加載頁面時用戶的網(wǎng)絡(luò)突然斷掉生蚁,我們應(yīng)當(dāng)更改WebView的緩存策略。
ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo.isAvailable()) { settings.setCacheMode(WebSettings.LOAD_DEFAULT);//網(wǎng)絡(luò)正常時使用默認緩存策略} else { settings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);//網(wǎng)絡(luò)不可用時只使用緩存}
既然有緩存邦投,就要有緩存控制,與一相似的是我們也要建立緩存控制機制屯援,定期或接受服務(wù)器通知來進行緩存的清空或更新。
第三個狞洋,就是延遲加載和執(zhí)行js
在WebView中,onPageFinished()的回調(diào)意味著頁面加載的完成庐橙。但該方法會在JavScript腳本執(zhí)行完成后才會觸發(fā)借嗽,倘若我們要加載的頁面使用了JQuery,會在處理完DOM對象恶导,執(zhí)行完$(document).ready(function() {})后才會渲染并顯示頁面。這是不可接受的兆蕉,所以我們需要對Js進行延遲加載,當(dāng)然這部分是Web前端的工作虎韵。
如果說還有什么
那就是JsBridge一律不得濫用缸废,這個對頁面加載的完成速度是有很大影響的,倘若一個頁面很多操作都通過JSbridge來控制企量,再怎么優(yōu)化也無濟于事(因為畢竟有那么多操作要實際執(zhí)行)。同時要注意的是硅瞧,不管你是否對資源進行緩存恕汇,都請將資源在服務(wù)器端進行壓縮。因為無論是資源的獲取和更新瘾英,都是要從服務(wù)器獲取的,所以對于資源文件的壓縮其實是最直接也最應(yīng)該做的事情之一但惶,但是一般服務(wù)器端都會做好,所以主要就是上面這三件事膀曾。
駕照考試
介紹了這么多,希望能對你有點幫助添谊。接下來時純實戰(zhàn)時間,我會將上面所介紹的很多知識點在接下來的代碼里實際應(yīng)用一遍纲缓,希望能夠帶給你更加直觀的使用感受喊废。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/ThemeOverlay.AppCompat.Light" /> </android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
Java部分
public class MainActivity extends AppCompatActivity {
private WebView mWebView;
private ProgressBar mProgressbar;
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initAppBar();//初始化Toolbar
initWebView();//初始化WebView
initWebSettings();//初始化WebSettings
initWebViewClient();//初始化WebViewClient
initWebChromeClient();//初始化WebChromeClient
}
private void initAppBar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitle("載入中..");
mToolbar.setTitleTextColor(getResources().getColor(R.color.colorWhite));
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
private void initWebView() {
mWebView = (WebView) findViewById(R.id.web_view);
mProgressbar = (ProgressBar) findViewById(R.id.progress_bar);
String url = "https://www.google.com";
mWebView.loadUrl(url);
}
private void initWebSettings() {
WebSettings settings = mWebView.getSettings();
//支持獲取手勢焦點
mWebView.requestFocusFromTouch();
//支持JS
settings.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時為webview設(shè)置節(jié)點
settings.setNeedInitialFocus(true);
//支持自動加載圖片
if (Build.VERSION.SDK_INT >= 19) {
settings.setLoadsImagesAutomatically(true);
} else {
settings.setLoadsImagesAutomatically(false);
}
settings.setNeedInitialFocus(true);
//設(shè)置編碼格式
settings.setDefaultTextEncodingName("UTF-8");
}
private void initWebViewClient() {
mWebView.setWebViewClient(new WebViewClient() {
//頁面開始加載時
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
mProgressbar.setVisibility(View.VISIBLE);
}
//頁面完成加載時
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mProgressbar.setVisibility(View.GONE);
}
//是否在WebView內(nèi)加載新頁面
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.toString());
return true;
}
//網(wǎng)絡(luò)錯誤時回調(diào)的方法
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
/**
* 在這里寫網(wǎng)絡(luò)錯誤時的邏輯,比如顯示一個錯誤頁面
*
* 這里我偷個懶不寫了
* */
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
}
});
}
private void initWebChromeClient() {
mWebView.setWebChromeClient(new WebChromeClient() {
private Bitmap mDefaultVideoPoster;//默認的視頻展示圖
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
setToolbarTitle(title);
}
@Override
public Bitmap getDefaultVideoPoster() {
if (mDefaultVideoPoster == null) {
mDefaultVideoPoster = BitmapFactory.decodeResource(
getResources(), R.drawable.video_default
);
return mDefaultVideoPoster;
}
return super.getDefaultVideoPoster();
}
});
}
/**
* 設(shè)置Toolbar標(biāo)題
*
* @param title
*/
private void setToolbarTitle(final String title) {
Log.d("setToolbarTitle", " WebDetailActivity " + title);
if (mToolbar != null) {
mToolbar.post(new Runnable() {
@Override
public void run() {
mToolbar.setTitle(TextUtils.isEmpty(title) ? getString(R.string.loading) : title);
}
});
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.page_up:
Toast.makeText(getApplicationContext(), "頁面向上", Toast.LENGTH_SHORT).show();
mWebView.pageUp(true);
break;
case R.id.page_down:
Toast.makeText(getApplicationContext(), "頁面向下", Toast.LENGTH_SHORT).show();
mWebView.pageDown(true);
break;
case R.id.refresh:
Toast.makeText(getApplicationContext(), "刷新~", Toast.LENGTH_SHORT).show();
mWebView.reload();
default:
return super.onOptionsItemSelected(item);
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//如果按下的是回退鍵且歷史記錄里確實還有頁面
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
} else {
Toast.makeText(getApplicationContext(), "考試結(jié)束,恭喜您考試合格!", Toast.LENGTH_LONG).show();
}
return super.onKeyDown(keyCode, event);
}
}