作者:Mino

1.針對(duì)加載webView中的資源時(shí)加快加載的速度優(yōu)化(主要是針對(duì)圖片)
原因:html代碼下載到WebView后,webkit開始解析網(wǎng)頁各個(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ò)情況較差的情況下,過多的網(wǎng)絡(luò)請(qǐng)求就會(huì)造成帶寬緊張,影響到css或js文件加載完成的時(shí)間,造成頁面空白loading過久。
解決方法:告訴WebView先不要自動(dòng)加載圖片,等頁面finish后再發(fā)起圖片加載。
//設(shè)置是否開啟密碼保存功能,不建議開啟,默認(rèn)已經(jīng)做了處理,存在盜取密碼的危險(xiǎn)
WebView.setSavePassword(false);
2.WebView硬件加速導(dǎo)致頁面渲染閃爍
原因:4.0以上的系統(tǒng)我們開啟硬件加速后,WebView渲染頁面更加快速,拖動(dòng)也更加順滑。但有個(gè)副作用就是,當(dāng)WebView視圖被整體遮住一塊,然后突然恢復(fù)時(shí)(比如使用SlideMenu將WebView從側(cè)邊滑出來時(shí)),這個(gè)過渡期會(huì)出現(xiàn)白塊同時(shí)界面閃爍。
解決方法:是在過渡期前將WebView的硬件加速臨時(shí)關(guān)閉,過渡期后再開啟。
/**
* 請(qǐng)求網(wǎng)絡(luò)出現(xiàn)error
* @param view view
* @param errorCode 錯(cuò)誤
* @param description description
* @param failingUrl 失敗鏈接
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (errorCode == 404) {
//用JAVAscript隱藏系統(tǒng)定義的404頁面信息
String data = "Page NO FOUND!";
view.loadUrl("JavaScript:document.body.innerHTML="" + data + """);
} else {
if (webListener!=null){
webListener.showErrorView();
}
}
}
// 向主機(jī)應(yīng)用程序報(bào)告Web資源加載錯(cuò)誤。這些錯(cuò)誤通常表明無法連接到服務(wù)器。
// 值得注意的是,不同的是過時(shí)的版本的回調(diào),新的版本將被稱為任何資源(iframe,圖像等)
// 不僅為主頁。因此,建議在回調(diào)過程中執(zhí)行最低要求的工作。
// 6.0 之后
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
X5WebUtils.log("服務(wù)器異常"+error.getDescription().toString());
}
//ToastUtils.showToast("服務(wù)器異常6.0之后");
//當(dāng)加載錯(cuò)誤時(shí),就讓它加載本地錯(cuò)誤網(wǎng)頁文件
//mWebView.loadUrl("file:///Android_asset/errorpage/error.html");
if (webListener!=null){
webListener.showErrorView();
}
}
/**
* 這個(gè)方法主要是監(jiān)聽標(biāo)題變化操作的
* @param view view
* @param title 標(biāo)題
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title.contains("404") || title.contains("網(wǎng)頁無法打開")){
if (webListener!=null){
webListener.showErrorView();
}
} else {
// 設(shè)置title
}
}
3.可以提前顯示加載進(jìn)度條
原因:WebView.loadUrl("url") 不會(huì)立馬就回調(diào) onPageStarted 或者 onProgressChanged 因?yàn)樵谶@一時(shí)間段,WebView 有可能在初始化內(nèi)核,也有可能在與服務(wù)器建立連接,這個(gè)時(shí)間段容易出現(xiàn)白屏,白屏用戶體驗(yàn)是很糟糕的。
解決方法:提前顯示進(jìn)度條雖然不是提升性能 , 但是對(duì)用戶體驗(yàn)來說也是很重要的一點(diǎn)。
/**
* 在加載資源時(shí)通知主機(jī)應(yīng)用程序發(fā)生SSL錯(cuò)誤
* 作用:處理https請(qǐng)求
* @param view view
* @param handler handler
* @param error error
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (error!=null){
String url = error.getUrl();
}
//https忽略證書問題
if (handler!=null){
//表示等待證書響應(yīng)
handler.proceed();
// handler.cancel(); //表示掛起連接,為默認(rèn)方式
// handler.handleMessage(null); //可做其他處理
}
}
4.WebView密碼明文存儲(chǔ)漏洞優(yōu)化
原因:WebView 默認(rèn)開啟密碼保存功能 mWebView.setSavePassword(true),如果該功能未關(guān)閉,在用戶輸入密碼時(shí),會(huì)彈出提示框,詢問用戶是否保存密碼,如果選擇”是”,密碼會(huì)被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險(xiǎn)。
解決方法:通過 WebSettings.setSavePassword(false) 關(guān)閉密碼保存提醒功能。
@Override
protected void onDestroy() {
try {
//有音頻播放的web頁面的銷毀邏輯
//在關(guān)閉了Activity時(shí),如果Webview的音樂或視頻,還在播放。就必須銷毀Webview
//但是注意:webview調(diào)用destory時(shí),webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時(shí)傳入了該Activity的context對(duì)象
//因此需要先從父容器中移除webview,然后再銷毀webview:
if (webView != null) {
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView = null;
}
} catch (Exception e) {
}
super.onDestroy();
}
5.自定義加載異常error的狀態(tài)頁面,比如下面這些方法中可能會(huì)出現(xiàn)error
原因:當(dāng)WebView加載頁面出錯(cuò)時(shí)(一般為404 NOT FOUND,Android WebView會(huì)默認(rèn)顯示一個(gè)出錯(cuò)界面。當(dāng)WebView加載出錯(cuò)時(shí),會(huì)在WebViewClient實(shí)例中的onReceivedError(),還有onReceivedTitle方法接收到錯(cuò)誤。
解決方法:自定義錯(cuò)誤頁面樣式。
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host = Uri.parse(url).getHost();
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名單內(nèi),非法網(wǎng)址,這個(gè)時(shí)候給用戶強(qiáng)烈而明顯的提示
} else {
//合法網(wǎng)址
}
}
}
6. WebView加載證書錯(cuò)誤
原因:webView加載一些別人的url時(shí)候,有時(shí)候會(huì)發(fā)生證書認(rèn)證錯(cuò)誤的情況。
解決方法:要將正常的呈現(xiàn)頁面給用戶,我們需要忽略證書錯(cuò)誤,需要調(diào)用WebViewClient類的onReceivedSslError方法,調(diào)用handler.proceed()來忽略該證書錯(cuò)誤。
//在onResume里面設(shè)置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
//在onStop里面設(shè)置setJavaScriptEnabled(false);
@Override
protected void onStop() {
super.onStop();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(false)
}
}
7.WebView音頻播放銷毀后還有聲音
原因:WebView頁面中播放了音頻,退出Activity后音頻仍然在播放。
解決方法:需要在Activity的onDestory()中從父容器中移除WebView。
@Override
protected void onDestroy() {
try {
//有音頻播放的web頁面的銷毀邏輯
//在關(guān)閉了Activity時(shí),如果Webview的音樂或視頻,還在播放。就必須銷毀Webview
//但是注意:webview調(diào)用destory時(shí),webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時(shí)傳入了該Activity的context對(duì)象
//因此需要先從父容器中移除webview,然后再銷毀webview:
if (webView != null) {
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView = null;
}
} catch (Exception e) {
}
super.onDestroy();
}
8.如何設(shè)置白名單操作
原因:客戶端內(nèi)的WebView都是可以通過客戶端的某個(gè)schema打開的,而要打開頁面的URL很多都并不寫在客戶端內(nèi),而是可以由URL中的參數(shù)傳遞過去的。上面4.0.5 使用scheme協(xié)議打開鏈接風(fēng)險(xiǎn)已經(jīng)說明了scheme使用的危險(xiǎn)性。
解決方法:設(shè)置運(yùn)行訪問的白名單,或者當(dāng)用戶打開外部鏈接前給用戶強(qiáng)烈而明顯的提示。設(shè)置白名單操作其實(shí)和過濾廣告是一個(gè)意思,這里你可以放一些合法的網(wǎng)址允許訪問。
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host = Uri.parse(url).getHost();
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名單內(nèi),非法網(wǎng)址,這個(gè)時(shí)候給用戶強(qiáng)烈而明顯的提示
} else {
//合法網(wǎng)址
}
}
}
9.Android后臺(tái)無法釋放js導(dǎo)致發(fā)熱耗電
原因:有些手機(jī)你如果webView加載的html里,有一些js一直在執(zhí)行比如動(dòng)畫之類的東西,如果此刻webView 掛在了后臺(tái)這些資源是不會(huì)被釋放用戶也無法感知。導(dǎo)致一直占有cpu 耗電特別快。
解決方法:WebView在后臺(tái)的時(shí)候,會(huì)調(diào)用onStop方法,即此時(shí)關(guān)閉js交互,回到前臺(tái)調(diào)用onResume再開啟js交互。
//在onResume里面設(shè)置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
//在onStop里面設(shè)置setJavaScriptEnabled(false);
@Override
protected void onStop() {
super.onStop();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(false)
}
}
10.WebView加載網(wǎng)頁不顯示圖片
原因:WebView從Lollipop(5.0)開始webView默認(rèn)不允許混合模式, https當(dāng)中不能加載http資源, 而開發(fā)的時(shí)候可能使用的是https的鏈接,但是鏈接中的圖片可能是http的,所以顯示圖片失敗。
解決方案:需要設(shè)置開啟。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
mWebView.getSettings().setBlockNetworkImage(false);