背景:原生時(shí)間緊沒(méi)時(shí)間開(kāi)發(fā)任務(wù)量大的任務(wù),而前端又閑著打醬油
方案:原生+webview混合開(kāi)發(fā)
缺點(diǎn):對(duì)于比較復(fù)雜的頁(yè)面,webview在性能上力不從心;且與原生通信頻繁也增加了隱藏的工作量
優(yōu)點(diǎn):能自帶支持動(dòng)態(tài)更新(js),能充分利用人力
一:WebView介紹
webview是一個(gè)基于webkit引擎,展示web頁(yè)面的控件。Android上的webview在低版本和高版本采用了不同的webkit版本內(nèi)核,Android4.4(19)后直接使用了Chrome內(nèi)核;WebView控件功能強(qiáng)大,除了具有一般View的屬性和設(shè)置外,還可以對(duì)url請(qǐng)求,頁(yè)面加載,渲染,頁(yè)面交互進(jìn)行強(qiáng)大的處理。一般來(lái)說(shuō)webview可單獨(dú)使用,也可聯(lián)合其工具類一起使用
移動(dòng)應(yīng)用的主體是webview,主要以網(wǎng)頁(yè)語(yǔ)言編寫(xiě),穿插Native功能的Hybrid App開(kāi)發(fā)類型。激活webview為活躍狀態(tài),能正常執(zhí)行網(wǎng)頁(yè)的響應(yīng);當(dāng)webview 的頁(yè)面被失去焦點(diǎn)切換到后臺(tái)不可見(jiàn)狀態(tài)onPause時(shí),需要通知自己暫停所有的動(dòng)作,比如DOM的解析,plugin的執(zhí)行,JavaScript的執(zhí)行等
二:WebView的作用
1,顯示和渲染web頁(yè)面
2,直接使用本地assets或者網(wǎng)絡(luò)上的html文件作為布局
3,可和JavaScript進(jìn)行互相調(diào)用
三:WebView的使用方式
1,直接在布局文件里寫(xiě)死
2,動(dòng)態(tài)添加進(jìn)viewgroup中
注:不管以哪種方式,都必須注意webview的銷毀,否則可能會(huì)造成內(nèi)存泄漏最終導(dǎo)致內(nèi)存溢出crash
四:WebView常用方法及說(shuō)明
下面是WebView的一些常用的方法列舉,一些已經(jīng)過(guò)時(shí)的方法未列出
webview.loadUrl("www.baidu.com") //加載url
webview.loadurl("javascript:jsmethod()") //可以執(zhí)行js函數(shù),沒(méi)有返回值,其中jsmethod是js中定義的函數(shù)
webview.evaluateJavascript(String script, ValueCallback<String> resultCallback) //script同上,有返回值
webview.setWebViewClient(new WebViewClient()); //設(shè)置webviewclient,通過(guò)重寫(xiě)部分方法可以實(shí)現(xiàn)定制化的功能,攔截url,監(jiān)控資源加載狀態(tài)等
webview.setWebChromeClient(new WebChromeClient() //設(shè)置WebChromeClient,重寫(xiě)部分方法可以處理js的對(duì)話框,網(wǎng)址圖標(biāo),標(biāo)題和加載進(jìn)度等
webview.onPause() //webview的生命周期方法,可于activity的生命周期綁定調(diào)用,通知webkit內(nèi)核暫停所有的動(dòng)作,比如DOM的解析,plugin的執(zhí)行,JavaScript的執(zhí)行
webview.onResume //webview的生命周期方法,可于activity的生命周期綁定調(diào)用,通知webkit內(nèi)核恢復(fù)所有的動(dòng)作,比如DOM的解析,plugin的執(zhí)行,JavaScript的執(zhí)行
webview.pauseTimers() //和onPause搭配使用,針對(duì)全應(yīng)用程序的webview,會(huì)暫停所有webview的layout,parsing,JavaScriptTimer,可降低CPU功耗,達(dá)到省電的目的
webview.resumeTimers() //和onResume搭配使用,恢復(fù)pauseTimers時(shí)的所有動(dòng)作
webview.destroy() //銷毀webview,一般在activity的onDestroy方法中調(diào)用,注意調(diào)用時(shí)需要先在父容器中移除webview,然后再銷毀,如:( (ViewGroup) webView.getParent()).removeView(webView);
webview.stopLoading() //停止當(dāng)前加載
webview.clearMatches() //清除網(wǎng)頁(yè)查找的高亮匹配字符
webview.clearHistory() //清除當(dāng)前webview的歷史訪問(wèn)記錄
webview.clearCache(true) //清空網(wǎng)頁(yè)訪問(wèn)留下的緩存數(shù)據(jù)(內(nèi)存+磁盤(pán)),為false時(shí)只清除內(nèi)存的
webview.loadUrl("about:blank") //清空當(dāng)前加載
webview.removeAllViews() //清空所有子view
五:WebSettings的常用配置方法及說(shuō)明
下面是WebSettings的一些常用的方法列舉,一些已經(jīng)過(guò)時(shí)的方法就沒(méi)有寫(xiě)出來(lái)了
WebSettings settings = webView.getSettings();
String ua = settings.getUserAgentString();
// 添加userAgent,方便前端網(wǎng)頁(yè)判斷請(qǐng)求來(lái)源
settings.setUserAgentString(ua + "; app/1.0.0");
// 頁(yè)面中要與JavaScript交互,需要設(shè)置為true
settings.setJavaScriptEnabled(true);
// 設(shè)置允許JS彈窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
//保存表單數(shù)據(jù)
settings.setSaveFormData(true);
//將圖片調(diào)整到適合webview的大小
settings.setUseWideViewPort(true);
// 縮放至屏幕的大小
settings.setLoadWithOverviewMode(true);
//不支持縮放
settings.setSupportZoom(false);
//設(shè)置內(nèi)置的縮放控件,這個(gè)取決于setSupportZoom,如果setSupportZoom設(shè)置為false,則此處設(shè)置無(wú)效
settings.setBuildInZoomControls(true);
//設(shè)置可以訪問(wèn)文件,如果此處設(shè)置為false,則webview的input標(biāo)簽的type=‘file’將點(diǎn)擊無(wú)響應(yīng)
settings.setAllowFileAccess(true);
//支持多窗口
settings.setSupportMultipleWindows(true);
//支持內(nèi)容重新布局,實(shí)戰(zhàn)發(fā)現(xiàn)此處如果設(shè)置為NARROW_COLUMN會(huì)導(dǎo)致某些手機(jī)顯示不能撐滿寬度問(wèn)題
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//支持自動(dòng)加載圖片
settings.setLoadsImagesAutomatically(true);
//設(shè)置編碼格式
settings.setDefaultTextEncodingName("utf-8");
//設(shè)置默認(rèn)字體大小
settings.setDefaultFontSize(16);
//特別注意,5.1以上默認(rèn)禁止了https和http的混用,以下方式是開(kāi)啟
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
六:WebView常見(jiàn)問(wèn)題
1,引起內(nèi)存泄露
在使用webview中,不管是寫(xiě)在xml里還是動(dòng)態(tài)生成的,都會(huì)不可避免的將當(dāng)前的context傳入webview中,也就是webview持有當(dāng)前activity的引用。在我們關(guān)閉activity的時(shí)候,如果此時(shí)webview還在加載就會(huì)導(dǎo)致activity無(wú)法被回收,造成內(nèi)存泄漏;總結(jié)來(lái)說(shuō),就是由于多個(gè)對(duì)象的生命周期不一致導(dǎo)致的無(wú)法同生同滅
一般不管是動(dòng)態(tài)生成還是xml寫(xiě)死,只要處理好了引用持有問(wèn)題,就能有效的避免內(nèi)存泄漏;下面是我嘗試的方案,在工具類WebViewUtils.java里封裝好,在activity銷毀的時(shí)候調(diào)用
public static void release(WebView webView) {
if (webView != null) {
webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
webView.stopLoading();
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
webView.clearHistory();
webView.removeAllViews();
webView.destroy();
}
}
@Override
public void onDestroy() {
WebViewUtils.release(mWebView);
super.onDestroy();
}
2,加載白屏
由于白屏問(wèn)題的出現(xiàn)的多樣性,暫提供多種嘗試性方案,大家按需參考
1,清除webview緩存和記錄
webview.clearCache(true);
webview.clearHistory();
2,可以設(shè)置不啟用緩存
websettings.setAppCacheEnabled(false);
3,H5的一些控件標(biāo)簽不支持導(dǎo)致的白屏
websettings.setDomStorageEnabled(true);
4,xml啟用軟件加速
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layerType="software"
android:scrollbars="none" />
5,通過(guò)menifest的來(lái)配置,在目標(biāo)webview的activity設(shè)置
<activity
android:name="xxx"
android:hardwarAccelerated="false" />
3,安全&漏洞問(wèn)題
- Android 4.2前遠(yuǎn)程代碼執(zhí)行漏洞的問(wèn)題,又稱js注入
這個(gè)問(wèn)題也是老生常談的問(wèn)題,其原因是因?yàn)樵贏PI 17(Android 4.2)以前的系統(tǒng)版本,程序沒(méi)有正確限制使用addJavascriotInterface方法,遠(yuǎn)程攻擊者可通過(guò)使用Java Reflection API利用該漏洞執(zhí)行任意Java對(duì)象的方法,可能出現(xiàn)手機(jī)被安裝木馬程序,發(fā)送扣費(fèi)短信,通信錄或者短信被竊取,甚至手機(jī)被遠(yuǎn)程控制等安全問(wèn)題
解決方案:
1,設(shè)置minSdkVersion大于等于17,即不能再低于4.2的手機(jī)上運(yùn)行
2,在需要被調(diào)用的Java方法上增加注解聲明:@JavascriptInterface
- webview明文存儲(chǔ)密碼的問(wèn)題
webview組建默認(rèn)開(kāi)啟了密碼保護(hù)功能,會(huì)提示用戶是否保存密碼,當(dāng)用戶選擇保存后,用戶名和密碼會(huì)被以明文形式保存到應(yīng)用數(shù)據(jù)目錄databases/webview.db中。攻擊者可能通過(guò)root的方式訪問(wèn)該應(yīng)用的WebView數(shù)據(jù)庫(kù),從而竊取本地明文存儲(chǔ)的用戶名和密碼
解決方案:
調(diào)用webview.getSettings().setSavePassword(false);,讓webview不再存儲(chǔ)密碼
- 有風(fēng)險(xiǎn)的系統(tǒng)隱藏接口問(wèn)題
根據(jù)CVE披露的webview的遠(yuǎn)程代碼執(zhí)行漏洞信息,Android系統(tǒng)中存在一共三個(gè)有遠(yuǎn)程代碼執(zhí)行漏洞的隱藏接口。分別位于android/webkit/webview中的searchBoxJavaBridge接口,位于android/webkit/AccessibilityInjector的accessibility接口和accessibilityTraversal,調(diào)用此三個(gè)接口的APP在開(kāi)啟輔助功能選項(xiàng)中第三方服務(wù)的Android系統(tǒng)上將面臨遠(yuǎn)程代碼執(zhí)行漏洞
解決方案:
需要顯示移除這三個(gè)接口
webview.removeJavascriptInterface("xxx");