溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android中WebView的基本配置與填坑記錄大全

發(fā)布時(shí)間:2020-09-10 14:18:35 來源:腳本之家 閱讀:269 作者:daisy 欄目:移動(dòng)開發(fā)

前言

在應(yīng)用程序開發(fā)過程中,經(jīng)常會(huì)采用webview來展現(xiàn)某些界面,這樣就可以不受發(fā)布版本控制,實(shí)時(shí)更新,遇到問題可以快速修復(fù)。

但是在Android開發(fā)中,由于Android版本分化嚴(yán)重,每一個(gè)版本針對(duì)webview都有部分更改,因此在開發(fā)過程中會(huì)遇到各種各樣的坑,下面這篇就來給大家介紹關(guān)于Android中WebView的基本配置與填坑記錄,話不多說了,來一起看看詳細(xì)的介紹吧。

基本配置

// 硬件加速
getActivity().getWindow().setFlags(
 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
// WebView 配置
WebSettings webSettings = mWebView.getSettings();
// 生命周期
mWebView.onPause(); // 通過 onPause 動(dòng)作通知內(nèi)核暫停所有的動(dòng)作,如 DOM 的解析、plugin 的執(zhí)行、JavaScript 執(zhí)行等
mWebView.onResume(); // 恢復(fù) WebView,能正常執(zhí)行網(wǎng)頁的響應(yīng)
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy(); // 當(dāng) Activity 要 destroy 時(shí),應(yīng)先將 WebView 移除,再 destroy 掉
// 前進(jìn)后退
if (mWebView.canGoBack()) {
 mWebView.goBack();
}
if (mWebView.canGoForward()) {
 mWebView.goForward();
}
// 緩存相關(guān)
mWebView.clearCache(true); // 清除緩存
mWebView.clearHistory(); // 清除歷史
mWebView.clearFormData(); // 清除表單數(shù)據(jù)
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);// 設(shè)置緩存模式
// 緩存模式
LOAD_DEFAULT: 默認(rèn),根據(jù) cache-control 決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)
LOAD_NORMAL: API level 17 中已經(jīng)廢棄, 從API level 11開始作用同 LOAD_DEFAULT 模式
LOAD_CACHE_ELSE_NETWORK: 只要本地有,無論是否過期,或者 no-cache,都使用緩存中的數(shù)據(jù)
LOAD_NO_CACHE: 不使用緩存,只從網(wǎng)絡(luò)獲取數(shù)據(jù)
LOAD_CACHE_ONLY: 不使用網(wǎng)絡(luò),只讀取本地緩存數(shù)據(jù)
// js 相關(guān)
webSettings.setJavaScriptEnabled(true); // 支持 js。如果碰到后臺(tái)無法釋放 js 導(dǎo)致耗電,應(yīng)在 onStop 和 onResume 里分別設(shè)成 false 和 true 
mWebView.addJavascriptInterface(new WebAppInterface(this), "android"); // js 接口
webSettings.setPluginsEnabled(true); // 支持插件
// 設(shè)置自適應(yīng)屏幕,兩者合用
webSettings.setUseWideViewPort(true); // 將圖片調(diào)整到適合 WebView 的大小 
webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小
// 縮放操作
webSettings.setSupportZoom(true); // 支持縮放,默認(rèn)為 true
webSettings.setBuiltInZoomControls(true); // 設(shè)置內(nèi)置的縮放控件,若為 false,則該 WebView 不可縮放
webSettings.setDisplayZoomControls(false); // 隱藏原生的縮放控件

填坑記錄

1、WebViewClient 類常用方法

mWebView.setWebViewClient(new MyWebViewClient());

shouldOverrideUrlLoading()

在網(wǎng)頁上的所有加載都經(jīng)過這個(gè)方法,這個(gè)函數(shù)我們可以做很多操作。

onPageStarted()

開始載入頁面調(diào)用的,我們可以設(shè)定一個(gè) loading 的頁面,告訴用戶程序在等待網(wǎng)絡(luò)響應(yīng)。

onPageFinished()

在頁面加載結(jié)束時(shí)調(diào)用。我們可以關(guān)閉 loading 條,切換程序動(dòng)作。

onLoadResource()

在加載頁面資源時(shí)會(huì)調(diào)用,每一個(gè)資源(比如圖片)的加載都會(huì)調(diào)用一次。

onReceivedError()

加載頁面出現(xiàn)錯(cuò)誤時(shí)調(diào)用。

2、WebChromeClient 類常用方法

mWebView.setWebChromeClient(new MyWebChromeClient());

onProgressChanged()

獲得網(wǎng)頁的加載進(jìn)度并顯示。

onReceivedTitle()

獲取 Web 頁中的標(biāo)題。

onJsAlert()

支持 javascript 的警告框。

onJsConfirm()

支持 javascript 的確認(rèn)框。

onJsPrompt()

支持 javascript 輸入框。

3、 Https 和 Http 混合模式

從 Android 5.0 開始,WebView 默認(rèn)不支持同時(shí)加載 Https 和 Http 資源。

解決方法:在webview加載頁面之前,設(shè)置加載模式為 MIXED_CONTENT_ALWAYS_ALLOW

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

4、安全問題

addJavascriptInterface

Android 4.2 以前,要采用攔截 prompt() 的方式進(jìn)行漏洞修復(fù);Android 4.2 以后,只需要對(duì)被調(diào)用的函數(shù)以 @JavascriptInterface 進(jìn)行注解。

searchBoxJavaBridge_、accessibility、accessibilityTraversal

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
 mWebView.removeJavascriptInterface("searchBoxJavaBridge_");
 mWebView.removeJavascriptInterface("accessibility");
 mWebView.removeJavascriptInterface("accessibilityTraversal");
}

密碼明文存儲(chǔ)漏洞

webSettings.setSavePassword(false);

5、替換 WebView 的加載錯(cuò)誤頁面

@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
 super.onReceivedError(view, errorCode, description, failingUrl);
 showCustErrorPage();
}
private View mErrorView;
protected void showCustErrorPage() {
 // 移除WebView
 ViewGroup webParentView = (ViewGroup) mWebViewInstance.getParent();
 while (webParentView.getChildCount() > 0) {
 webParentView.removeViewAt(0);
 }
 // 生成自定義錯(cuò)誤頁面
 if (mErrorView == null) {
 mErrorView = View.inflate(mContext, R.layout.webview_error, null);
 mErrorView.setOnClickListener(new View.OnClickListener() {
  public void onClick(View v) {
  showWebViewPage();
  mWebViewInstance.reload();
  }
 });
 }
 // 替換為錯(cuò)誤頁面
 ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
  ViewPager.LayoutParams.FILL_PARENT, ViewPager.LayoutParams.FILL_PARENT);
 webParentView.addView(mErrorView, 0, lp);
}
protected void showWebViewPage() {
 // 移除自定義錯(cuò)誤頁面
 ViewGroup webParentView = (ViewGroup) mErrorView.getParent();
 while (webParentView.getChildCount() > 0) {
 webParentView.removeViewAt(0);
 }
 // 替換為WebView
 ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
  ViewPager.LayoutParams.FILL_PARENT, ViewPager.LayoutParams.FILL_PARENT);
 webParentView.addView(mWebViewInstance, 0, lp);
}

6、WebView的內(nèi)存泄露。

關(guān)于這個(gè)問題,我很難給你一個(gè)清晰的描述,你在谷歌里搜 webview lead memory 能搜到很多結(jié)果 甚至還有給谷歌提交的issue 哈哈,我也無法給出一個(gè)清晰的答案 在什么時(shí)候 什么版本那些手機(jī)上一定會(huì)出現(xiàn)內(nèi)存泄露,

但是根據(jù)我自己的monkey結(jié)果來看,有時(shí),webview內(nèi)存泄露的情況還是很嚴(yán)重的,尤其是當(dāng)你加載的頁面比較龐大的時(shí)候。解決方案 我查了很多也用了很多,但是都不太理想,最后看了下微信和qq的做法,試了一下是目前效果最好的,

就是 當(dāng)你要用webview的時(shí)候,記得最好 另外單獨(dú)開一個(gè)進(jìn)程 去使用webview 并且當(dāng)這個(gè) 進(jìn)程結(jié)束時(shí),請(qǐng)手動(dòng)調(diào)用System.exit(0)。

這是目前對(duì)于webview 內(nèi)存泄露 最好的解決方案。使用此方法 所有因?yàn)閣ebview引發(fā)的 資源無法釋放等問題 全部可以解決。

7、getSettings().setBuiltInZoomControls(true) 引發(fā)的crush。

這個(gè)方法調(diào)用以后 如果你觸摸屏幕 彈出那個(gè)提示框還沒消失的時(shí)候 你如果activity結(jié)束了 就會(huì)報(bào)錯(cuò)了。3.0以上 4.4以下很多手機(jī)會(huì)出現(xiàn)這種情況

所以為了規(guī)避他,我們通常是在activity的onDestroy方法里手動(dòng)的將webiew設(shè)置成 setVisibility(View.GONE)

8、onPageFinished 函數(shù)到底有用沒有?

多數(shù)開發(fā)者都是參考的http://stackoverflow.com/questions/3149216/how-to-listen-for-a-webview-finishing-loading-a-url-in-android 這個(gè)上面的高票答案。

但其實(shí)根據(jù)我自己觀察,這個(gè)函數(shù)并沒有什么卵用,有的時(shí)候是提前結(jié)束,有的時(shí)候就遲遲無法結(jié)束,你信這個(gè)函數(shù) 還不如信上帝,甚至于onProgressChanged這個(gè)函數(shù)

都比onPageFinished 要準(zhǔn)一些。如果你的產(chǎn)品經(jīng)理堅(jiān)持你一定要實(shí)現(xiàn)這種功能的話,我建議你 提早結(jié)束他,否則卡在那用戶遲遲動(dòng)不了 這種體驗(yàn)不好。

有空的同學(xué)可以跟一下源碼,onPageFinished 在不同的內(nèi)核里 調(diào)用的時(shí)機(jī)都不一樣。說實(shí)話 我也很醉。。。這個(gè)問題 有完美解決方案的 請(qǐng)知會(huì)我一下。。。

9、后臺(tái)無法釋放js 導(dǎo)致耗電。

這個(gè)可能很少有人知道,我也是被投訴過 才了解,在有的手機(jī)里,你如果webview加載的html里 有一些js 一直在執(zhí)行比如動(dòng)畫之類的東西,如果此刻webview 掛在了后臺(tái)

這些資源是不會(huì)被釋放 用戶也無法感知。。。導(dǎo)致一直占有cpu 耗電特別快,所以大家記住了,如果遇到這種情況 請(qǐng)?jiān)趏nstop和onresume里分別把setJavaScriptEnabled();

給設(shè)置成false和true。

10、如果實(shí)在不想用開額外進(jìn)程的方式解決webview 內(nèi)存泄露的問題,那么下面的方法很大程度上可以避免這種情況

public void releaseAllWebViewCallback() {
  if (android.os.Build.VERSION.SDK_INT < 16) {
   try {
    Field field = WebView.class.getDeclaredField("mWebViewCore");
    field = field.getType().getDeclaredField("mBrowserFrame");
    field = field.getType().getDeclaredField("sConfigCallback");
    field.setAccessible(true);
    field.set(null, null);
   } catch (NoSuchFieldException e) {
    if (BuildConfig.DEBUG) {
     e.printStackTrace();
    }
   } catch (IllegalAccessException e) {
    if (BuildConfig.DEBUG) {
     e.printStackTrace();
    }
   }
  } else {
   try {
    Field sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback");
    if (sConfigCallback != null) {
     sConfigCallback.setAccessible(true);
     sConfigCallback.set(null, null);
    }
   } catch (NoSuchFieldException e) {
    if (BuildConfig.DEBUG) {
     e.printStackTrace();
    }
   } catch (ClassNotFoundException e) {
    if (BuildConfig.DEBUG) {
     e.printStackTrace();
    }
   } catch (IllegalAccessException e) {
    if (BuildConfig.DEBUG) {
     e.printStackTrace();
    }
   }
  }
 }

在webview的 destroy方法里 調(diào)用這個(gè)方法就行了。

11、另外很多人 不知道webview 實(shí)際上有自己一套完整的cookie機(jī)制的,利用好這個(gè) 可以大大增加對(duì)客戶端的訪問速度。

Android中WebView的基本配置與填坑記錄大全

實(shí)際上cookie就是存放在這個(gè)表里的。

很多人都想要一個(gè)效果:網(wǎng)頁更新cookie 設(shè)置完cookie以后 不刷新頁面即可生效。這個(gè)在2.3以下和2.3以上要實(shí)現(xiàn)的方法不太一樣,所以要做一次兼容

public void updateCookies(String url, String value) {
  if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { // 2.3及以下
   CookieSyncManager.createInstance(getContext().getApplicationContext());
  }
  CookieManager cookieManager = CookieManager.getInstance();
  cookieManager.setAcceptCookie(true);
  cookieManager.setCookie(url, value);
  if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
   CookieSyncManager.getInstance().sync();
  }
 }

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI