Android WebView 性能优化
原文出處:http://motalks.cn/2016/09/11/Android-WebView-JavaScript-3/
WebView相關閱讀
- Android WebView 和 javaScript的互相調用(一)
- Android WebView 和 javaScript的互相調用(二)
- Android WebView 和 javaScript的互相調用(三)
- Android WebView與js交互通信
- Android 4.4 中 WebView 使用注意事項
- Android WebView開發問題匯總
- Android WebView 性能優化
Mo說:大家通過前兩篇文章想必都能順利的 get 到 WebView 與 JavaScript 交互的技能了。現在 App 嵌入 H5 頁面已經是稀松平常的事情了,開發者要面對 WebView 也越來越多的爆發出來,比如頁面加載慢,內存泄露,不同 Android 系統版本采用了不同內核的兼容問題等等。 所以當我們使用了 WebView 這個組件的時候,性能優化的事情就不能不提上議程了。這篇文章我們就針對上述問題來總結下 Android WebView 性能優化的常見方法。
一、頁面加載速度優化
影響頁面加載速度的因素有非常多,我們在對 WebView 加載一個網頁的過程進行調試發現,每次加載的過程中都會有較多的網絡請求,除了 web 頁面自身的 URL 請求,還會有 web 頁面外部引用的JS、CSS、字體、圖片等等都是個獨立的 http 請求。這些請求都是串行的,這些請求加上瀏覽器的解析、渲染時間就會導致 WebView 整體加載時間變長,消耗的流量也對應的真多。接下來我們就來說說幾種優化方案來是怎么解決這個問題的。
二、選擇合適的 WebView 緩存
WebView 緩存看似就是開啟幾個開關的問題,但是要弄懂這幾種緩存機制還是很有深度。下圖是騰訊某工程師總結六種 H5 常用的緩存機制的優勢及適用場景。
1、瀏覽器緩存機制:
主要前端負責,Android 端不需要進行特別的配置。
2、Dom Storage(Web Storage)存儲機制:
配合前端使用,使用時需要打開 DomStorage 開關。
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setDomStorageEnabled(true);3、Web SQL Database 存儲機制:
雖然已經不推薦使用了,但是為了兼容性,還是提供下 Android 端使用的方法
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setDatabaseEnabled(true); final String dbPath = getApplicationContext().getDir("db",Context.MODE_PRIVATE).getPath(); webSettings.setDatabasePath(dbPath)4、Application Cache 存儲機制
Application Cache(簡稱 AppCache)似乎是為支持 Web App 離線使用而開發的緩存機制。它的緩存機制類似于瀏覽器的緩存(Cache-Control 和 Last-Modified)機制,都是以文件為單位進行緩存,且文件有一定更新機制。但 AppCache 是對瀏覽器緩存機制的補充,不是替代。
不過根據官方文檔,AppCache 已經不推薦使用了,標準也不會再支持。現在主流的瀏覽器都是還支持 AppCache的,以后就不太確定了。同樣給出 Android 端啟用 AppCache 的代碼。
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setAppCacheEnabled(true); final String cachePath = getApplicationContext().getDir("cache",Context.MODE_PRIVATE).getPath(); webSettings.setAppCachePath(cachePath); webSettings.setAppCacheMaxSize(5*1024*1024);5、Indexed Database 存儲機制
IndexedDB 也是一種數據庫的存儲機制,但不同于已經不再支持的 Web SQL Database。IndexedDB 不是傳統的關系數據庫,可歸為 NoSQL 數據庫。IndexedDB 又類似于 Dom Storage 的 key-value 的存儲方式,但功能更強大,且存儲空間更大。
Android 在4.4開始加入對 IndexedDB 的支持,只需打開允許 JS 執行的開關就好了。
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true);6、File System API
File System API 是 H5 新加入的存儲機制。它為 Web App 提供了一個虛擬的文件系統,就像 Native App 訪問本地文件系統一樣。由于安全性的考慮,這個虛擬文件系統有一定的限制。Web App 在虛擬的文件系統中,可以進行文件(夾)的創建、讀、寫、刪除、遍歷等操作。很可惜到目前,Android 系統的 WebView 還不支持 File System API。
簡單的介紹完了上面六種 H5 常用的緩存模式,想必大家能對 Android WebView 所支持的緩存模式有個粗略的了解。如果想和前端更好的配合使用 Android WebView 所支持的緩存,建議看下這篇文章《H5 緩存機制淺析 移動端 Web 加載性能優化》
三、常用資源預加載
上面介紹的緩存技術,能優化二次啟動 WebView 的加載速度,那首次加載 H5 頁面的速度該怎么優化呢?上面分析了一次加載過程會有許多外部依賴的 JS、CSS、圖片等資源需要下載,那我們能不能提前將這些資源下載好,等H5 加載時直接替換呢?
好在從 API 11(Android 3.0)開始,WebView 引入了 shouldInterceptRequest 函數,這個函數有兩種重載。
public WebResourceResponse shouldInterceptRequest(WebView webView, String url) //從 API 11 引入,API 21 廢棄 public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) //從 API 21 開始引入考慮到目前大多數 App 還要支持 API 14,所以還是使用 shouldInterceptRequest (WebView view, String url) 為例。
WebView mWebView = (WebView) findViewById(R.id.webview); mWebView.setWebViewClient(new WebViewClient() {@Overridepublic WebResourceResponse shouldInterceptRequest(WebView webView, final String url) {WebResourceResponse response = null;// 檢查該資源是否已經提前下載完成。我采用的策略是在應用啟動時,用戶在 wifi 的網絡環境下 // 提前下載 H5 頁面需要的資源。boolean resDown = JSHelper.isURLDownValid(url);if (resDown) {jsStr = JsjjJSHelper.getResInputStream(url);if (url.endsWith(".png")) {response = getWebResourceResponse(url, "image/png", ".png");} else if (url.endsWith(".gif")) {response = getWebResourceResponse(url, "image/gif", ".gif");} else if (url.endsWith(".jpg")) {response = getWebResourceResponse(url, "image/jepg", ".jpg");} else if (url.endsWith(".jepg")) {response = getWebResourceResponse(url, "image/jepg", ".jepg");} else if (url.endsWith(".js") && jsStr != null) {response = getWebResourceResponse("text/javascript", "UTF-8", ".js");} else if (url.endsWith(".css") && jsStr != null) {response = getWebResourceResponse("text/css", "UTF-8", ".css");} else if (url.endsWith(".html") && jsStr != null) {response = getWebResourceResponse("text/html", "UTF-8", ".html");}}// 若 response 返回為 null , WebView 會自行請求網絡加載資源。 return response;}});private WebResourceResponse getWebResourceResponse(String url, String mime, String style) {WebResourceResponse response = null;try {response = new WebResourceResponse(mime, "UTF-8", new FileInputStream(new File(getJSPath() + TPMD5.md5String(url) + style)));} catch (FileNotFoundException e) {e.printStackTrace();}return response;}public String getJsjjJSPath() {String splashTargetPath = JarEnv.sApplicationContext.getFilesDir().getPath() + "/JS";if (!TPFileSysUtil.isDirFileExist(splashTargetPath)) {TPFileSysUtil.createDir(splashTargetPath);}return splashTargetPath + "/";}四、常用 JS 本地化及延遲加載
比預加載更粗暴的優化方法是直接將常用的 JS 腳本本地化,直接打包放入 apk 中。比如 H5 頁面獲取用戶信息,設置標題等通用方法,就可以直接寫入一個 JS 文件,放入 asserts 文件夾,在 WebView 調用了onPageFinished() 方法后進行加載。需要注意的是,在該 JS 文件中需要寫入一個 JS 文件載入完畢的事件,這樣前端才能接受都愛 JS 文件已經種植完畢,可以調用 JS 中的方法了。 附上一段本地化的 JS 代碼。
javascript: ; (function() {try{window.JSBridge = {'invoke': function(name) {var args = [].slice.call(arguments, 1),callback = args.pop(),params, obj = this[name];if (typeof callback !== 'function') {params = callback;callback = function() {}} else {params = args[0]} if (typeof obj !== 'object' || typeof obj.func !== 'function') {callback({'err_msg': 'system:function_not_exist'});return}obj.callback = callback;obj.params = params;obj.func(params)},'on': function(event, callback) {var obj = this['on' + event];if (typeof obj !== 'object') {callback({'err_msg': 'system:function_not_exist'});retrun}if (typeof callback !== 'undefined') obj.callback = callback},'login': {'func': function(params) {prompt("login", JSON.stringify(params))},'params': {},'callback': function(res) {}},'settitle': {'func': function(params) {prompt("settitle",JSON.stringify(params))},'params': {},'callback': function(res) {}},}catch(e){alert('demo.js error:'+e);}var readyEvent = document.createEvent('Events');readyEvent.initEvent('JSBridgeReady', true, true);document.dispatchEvent(readyEvent) })();六、關于 JS 延遲加載
Android 的 OnPageFinished 事件會在 Javascript 腳本執行完成之后才會觸發。如果在頁面中使 用JQuery,會在處理完 DOM 對象,執行完 $(document).ready(function() {}); 事件自會后才會渲染并顯示頁面。而同樣的頁面在 iPhone 上卻是載入相當的快,因為 iPhone 是顯示完頁面才會觸發腳本的執行。所以我們這邊的解決方案延遲 JS 腳本的載入,這個方面的問題是需要Web前端工程師幫忙優化的。
七、使用第三方 WebView 內核
WebView 的兼容性一直也是困擾我們 Android 開發者的一個大問題,不說 Android 4.4 版本 Google 使用了Chromium 替代 Webkit 作為 WebView 內核,就看看國內眾多的第三方 ROM 都有可能會對原生的 WebView 做出修改,這時候如果出現兼容問題,是非常難定位到問題和解決的。
在一次使用微信瀏覽訂閱公眾號文章的過程中,發現微信的 H5 頁面有一行 『QQ 瀏覽器 X5 內核提供技術支持』。順著這個線索我就找到了騰訊瀏覽服務。發現騰訊已經把這個功能開放了,而且集成的 SDK 很小只有212 KB。這是很驚人的,通過介紹才發現這個 SDK 是可以共享微信和手機 QQ 的 X5 內核。這就很方便了,作為國內市場最不可或缺的兩個 App,我們能只需要集成一個很小的 SDK 就可以共享使用 X5 內核了,不得不說騰訊還是很有想法的。
簡單摘錄些功能亮點,想必能讓大家高潮一番。詳細內容大家可以直接到騰訊瀏覽服務看看,我相信不會讓你們失望的。
網頁瀏覽能力
- Web頁面crash率降低75%
- 頁面打開速度提升35%
- 流量節省60%
閱讀模式
- 去除網頁中廣告等雜質
- 優化文章的閱讀體驗
文件打開能力
- 包括會話頁的互傳文件及郵件中附件
- 支持doc、ppt、xls、pdf等辦公格式
- 支持jpg、gif、png、bmp等圖片格式
- 支持zip、rar等壓縮文件
- 支持mp3、mp4、RMVB等音視頻格式
視頻菜單能力
- 支持屏幕調節等常規視頻菜單功能
- 靈活切換全屏&小窗功能
八、WebView 導致的內存泄露
Android 中的 WebView 存在很大的兼容性問題,不僅僅是 Android 系統版本的不同對 WebView 產生很大的差異,另外不同的廠商出貨的 ROM 里面 WebView 也存在著很大的差異。更嚴重的是標準的 WebView 存在內存泄露的問題,看這里WebView causes memory leak - leaks the parent Activity。所以通常根治這個問題的辦法是為 WebView 開啟另外一個進程,通過 AIDL 與主進程進行通信,WebView 所在的進程可以根據業務的需要選擇合適的時機進行銷毀,從而達到內存的完整釋放。
這段話來自胡凱翻譯的 Google Android 內存優化之 OOM 。這里提到的讓 WebView 獨立運行在一個進程里,用完 WebView 后直接銷毀這個進程,即使內存泄露了,也不會影響到主進程。微信,手 Q 等 App 也采用了這個方案。但是這就涉及到了跨進程通訊,處理起來就比較麻煩。
另外個解決方案,就是使用自己封裝的 WebView,比如上面提到的 X5 內核,且使用 WebView 的時候,不在 XML 里面聲明,而是在代碼中直接 new 出來,傳入 application context 來防止 activity 引用被濫用。
WebView webView = new WebView(getContext().getApplicationContext()); webFrameLayout.addView(webView, 0);在使用了這個方式后,基本上 90% 的 WebView 內存泄漏的問題便得以解決。
上面兩個方案,大家可以結合自己的項目情況選擇。另外對 WebView 內存泄露原因感興趣的可以看看這篇文章。
《Android中導致內存泄漏的竟然是它—-Dialog》
參考文章
《H5 緩存機制淺析 移動端 Web 加載性能優化》
《android內存優化之webview》
總結
以上是生活随笔為你收集整理的Android WebView 性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android WebView 和 ja
- 下一篇: 使用Palette类提取图片的颜色信息