您好,登錄后才能下訂單哦!
前言
在應用程序開發過程中,經常會采用webview來展現某些界面,這樣就可以不受發布版本控制,實時更新,遇到問題可以快速修復。
但是在Android開發中,由于Android版本分化嚴重,每一個版本針對webview都有部分更改,因此在開發過程中會遇到各種各樣的坑,下面這篇就來給大家介紹關于Android中WebView的基本配置與填坑記錄,話不多說了,來一起看看詳細的介紹吧。
基本配置
// 硬件加速 getActivity().getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); // WebView 配置 WebSettings webSettings = mWebView.getSettings(); // 生命周期 mWebView.onPause(); // 通過 onPause 動作通知內核暫停所有的動作,如 DOM 的解析、plugin 的執行、JavaScript 執行等 mWebView.onResume(); // 恢復 WebView,能正常執行網頁的響應 ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.destroy(); // 當 Activity 要 destroy 時,應先將 WebView 移除,再 destroy 掉 // 前進后退 if (mWebView.canGoBack()) { mWebView.goBack(); } if (mWebView.canGoForward()) { mWebView.goForward(); } // 緩存相關 mWebView.clearCache(true); // 清除緩存 mWebView.clearHistory(); // 清除歷史 mWebView.clearFormData(); // 清除表單數據 webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);// 設置緩存模式 // 緩存模式 LOAD_DEFAULT: 默認,根據 cache-control 決定是否從網絡上取數據 LOAD_NORMAL: API level 17 中已經廢棄, 從API level 11開始作用同 LOAD_DEFAULT 模式 LOAD_CACHE_ELSE_NETWORK: 只要本地有,無論是否過期,或者 no-cache,都使用緩存中的數據 LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據 LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據 // js 相關 webSettings.setJavaScriptEnabled(true); // 支持 js。如果碰到后臺無法釋放 js 導致耗電,應在 onStop 和 onResume 里分別設成 false 和 true mWebView.addJavascriptInterface(new WebAppInterface(this), "android"); // js 接口 webSettings.setPluginsEnabled(true); // 支持插件 // 設置自適應屏幕,兩者合用 webSettings.setUseWideViewPort(true); // 將圖片調整到適合 WebView 的大小 webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小 // 縮放操作 webSettings.setSupportZoom(true); // 支持縮放,默認為 true webSettings.setBuiltInZoomControls(true); // 設置內置的縮放控件,若為 false,則該 WebView 不可縮放 webSettings.setDisplayZoomControls(false); // 隱藏原生的縮放控件
填坑記錄
1、WebViewClient 類常用方法
mWebView.setWebViewClient(new MyWebViewClient());
shouldOverrideUrlLoading()
在網頁上的所有加載都經過這個方法,這個函數我們可以做很多操作。
onPageStarted()
開始載入頁面調用的,我們可以設定一個 loading 的頁面,告訴用戶程序在等待網絡響應。
onPageFinished()
在頁面加載結束時調用。我們可以關閉 loading 條,切換程序動作。
onLoadResource()
在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次。
onReceivedError()
加載頁面出現錯誤時調用。
2、WebChromeClient 類常用方法
mWebView.setWebChromeClient(new MyWebChromeClient());
onProgressChanged()
獲得網頁的加載進度并顯示。
onReceivedTitle()
獲取 Web 頁中的標題。
onJsAlert()
支持 javascript 的警告框。
onJsConfirm()
支持 javascript 的確認框。
onJsPrompt()
支持 javascript 輸入框。
3、 Https 和 Http 混合模式
從 Android 5.0 開始,WebView 默認不支持同時加載 Https 和 Http 資源。
解決方法:在webview加載頁面之前,設置加載模式為 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() 的方式進行漏洞修復;Android 4.2 以后,只需要對被調用的函數以 @JavascriptInterface 進行注解。
searchBoxJavaBridge_、accessibility、accessibilityTraversal
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { mWebView.removeJavascriptInterface("searchBoxJavaBridge_"); mWebView.removeJavascriptInterface("accessibility"); mWebView.removeJavascriptInterface("accessibilityTraversal"); }
密碼明文存儲漏洞
webSettings.setSavePassword(false);
5、替換 WebView 的加載錯誤頁面
@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); } // 生成自定義錯誤頁面 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(); } }); } // 替換為錯誤頁面 ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( ViewPager.LayoutParams.FILL_PARENT, ViewPager.LayoutParams.FILL_PARENT); webParentView.addView(mErrorView, 0, lp); } protected void showWebViewPage() { // 移除自定義錯誤頁面 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的內存泄露。
關于這個問題,我很難給你一個清晰的描述,你在谷歌里搜 webview lead memory 能搜到很多結果 甚至還有給谷歌提交的issue 哈哈,我也無法給出一個清晰的答案 在什么時候 什么版本那些手機上一定會出現內存泄露,
但是根據我自己的monkey結果來看,有時,webview內存泄露的情況還是很嚴重的,尤其是當你加載的頁面比較龐大的時候。解決方案 我查了很多也用了很多,但是都不太理想,最后看了下微信和qq的做法,試了一下是目前效果最好的,
就是 當你要用webview的時候,記得最好 另外單獨開一個進程 去使用webview 并且當這個 進程結束時,請手動調用System.exit(0)。
這是目前對于webview 內存泄露 最好的解決方案。使用此方法 所有因為webview引發的 資源無法釋放等問題 全部可以解決。
7、getSettings().setBuiltInZoomControls(true) 引發的crush。
這個方法調用以后 如果你觸摸屏幕 彈出那個提示框還沒消失的時候 你如果activity結束了 就會報錯了。3.0以上 4.4以下很多手機會出現這種情況
所以為了規避他,我們通常是在activity的onDestroy方法里手動的將webiew設置成 setVisibility(View.GONE)
8、onPageFinished 函數到底有用沒有?
多數開發者都是參考的http://stackoverflow.com/questions/3149216/how-to-listen-for-a-webview-finishing-loading-a-url-in-android 這個上面的高票答案。
但其實根據我自己觀察,這個函數并沒有什么卵用,有的時候是提前結束,有的時候就遲遲無法結束,你信這個函數 還不如信上帝,甚至于onProgressChanged這個函數
都比onPageFinished 要準一些。如果你的產品經理堅持你一定要實現這種功能的話,我建議你 提早結束他,否則卡在那用戶遲遲動不了 這種體驗不好。
有空的同學可以跟一下源碼,onPageFinished 在不同的內核里 調用的時機都不一樣。說實話 我也很醉。。。這個問題 有完美解決方案的 請知會我一下。。。
9、后臺無法釋放js 導致耗電。
這個可能很少有人知道,我也是被投訴過 才了解,在有的手機里,你如果webview加載的html里 有一些js 一直在執行比如動畫之類的東西,如果此刻webview 掛在了后臺
這些資源是不會被釋放 用戶也無法感知。。。導致一直占有cpu 耗電特別快,所以大家記住了,如果遇到這種情況 請在onstop和onresume里分別把setJavaScriptEnabled();
給設置成false和true。
10、如果實在不想用開額外進程的方式解決webview 內存泄露的問題,那么下面的方法很大程度上可以避免這種情況
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方法里 調用這個方法就行了。
11、另外很多人 不知道webview 實際上有自己一套完整的cookie機制的,利用好這個 可以大大增加對客戶端的訪問速度。
實際上cookie就是存放在這個表里的。
很多人都想要一個效果:網頁更新cookie 設置完cookie以后 不刷新頁面即可生效。這個在2.3以下和2.3以上要實現的方法不太一樣,所以要做一次兼容
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(); } }
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。