Android WebView:性能優化不得不說的事

2022-01-07 開發者全社區

相關閱讀:

吊炸天!74款APP完整源碼!

有了這些免費無限次的API 接口,再也不愁沒有伺服器開發不了APP了,也可以自己開發小程序了

github上萬顆star的21個JAVA(19個Android相關)開發框架,知道多少,用過多少(截止2016年12月17日)

原文連結:http://motalks.cn/2016/09/11/Android-WebView-JavaScript-3/

Mo說:大家通過前兩篇文章想必都能順利的 get 到 WebView 與 JavaScript 交互的技能了。現在 App 嵌入 H5 頁面已經是稀鬆平常的事情了,開發者要面對 WebView 也越來越多的爆發出來,比如頁面加載慢,內存洩露,不同 Android 系統版本採用了不同內核的兼容問題等等。 所以當我們使用了 WebView 這個組件的時候,性能優化的事情就不能不提上議程了。這篇文章我們就針對上述問題來總結下 Android WebView 性能優化的常見方法。

頁面加載速度優化

影響頁面加載速度的因素有非常多,我們在對 WebView 加載一個網頁的過程進行調試發現,每次加載的過程中都會有較多的網絡請求,除了 web 頁面自身的 URL 請求,還會有 web 頁面外部引用的JS、CSS、字體、圖片等等都是個獨立的 http 請求。這些請求都是串行的,這些請求加上瀏覽器的解析、渲染時間就會導致 WebView 整體加載時間變長,消耗的流量也對應的真多。接下來我們就來說說幾種優化方案來是怎麼解決這個問題的。

選擇合適的 WebView 緩存

WebView 緩存看似就是開啟幾個開關的問題,但是要弄懂這幾種緩存機制還是很有深度。下圖是騰訊某工程師總結六種 H5 常用的緩存機制的優勢及適用場景。


瀏覽器緩存機制:

主要前端負責,Android 端不需要進行特別的配置。

Dom Storage(Web Storage)存儲機制:

配合前端使用,使用時需要打開 DomStorage 開關。

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setDomStorageEnabled(true);

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)

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);

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);

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() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, final String url) {
WebResourceResponse response = null;

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");
}
}

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:我是怎麼和 JavaScript 互撩的?》

《Android WebView:JavaScript 調用效率哪家強?》

到這裡,Android WebView 系列文章就告一段落了,大家還有什麼想了解的或者對文中什麼知識點有疑惑的,歡迎留言探討。

關於Java和Android大牛頻道

Java和Android大牛頻道是一個數萬人關注的探討Java和Android開發的公眾號,分享和原創最有價值的乾貨文章,讓你成為這方面的大牛!

我們探討android和Java開發最前沿的技術:android性能優化 ,插件化,跨平臺,動態化,加固和反破解等,也討論設計模式/軟體架構等。由一群來自BAT的工程師組成的團隊

關注即送紅包,回覆:「百度」 、「阿里」、「騰訊」 有驚喜!!!關注後可用入微信群。群裡都是來自百度阿里騰訊的大牛。

歡迎關注我們,一起討論技術,掃描和長按下方的二維碼可快速關注我們。或搜索微信公眾號:JANiubility。

公眾號:JANiubility

相關焦點

  • Android中X5WebView詳解
    騎小豬看流星 的博客地址:https://www.jianshu.com/u/0111a7da544b這一篇的目標就是怎麼樣快速封裝X5WebView,如何有效的同步以及管理Cookie,使用IntentService優化預加載,如何監聽進度條等一些在項目中使用的常用功能。
  • 大量安卓APP離奇閃退,竟然是Webview的鍋!Google已經更新最新解決方案!
    似乎是因為新版的發布,導致webview組件內出現bug,導致了這次集體的APP崩潰事件,只需卸載Android WebView更新即可解決崩潰問題。image.pngGoogle 發布官方解決方案 在事故發生12小時之後,谷歌發言人在發給Express.co.uk的一份聲明中說:「我們已解決了WebView的問題,該問題導致
  • 關於 WebView 錯誤導致應用崩潰的信息更新
    id=com.google.android.webview)Select Update. If it doesn't say 「Update」, skip this step.Repeat these steps for Google Chrome (https://play.google.com/store/apps/details?
  • 關於Android的.so文件你所需要知道的
    你應該儘可能的提供專為每個ABI優化過的.so文件,但要麼全部支持,要麼都不支持:你不應該混合著使用。你應該為每個ABI目錄提供對應的.so文件。 當一個應用安裝在設備上,只有該設備支持的CPU架構對應的.so文件會被安裝。
  • SystemWebView「77.0.3865.116」安卓裝機必備
    更新說明77.0.3865.116正式版更新~ •錯誤修復和快速性能改進谷歌市場:  https://play.google.com/store/apps/details?id=com.google.android.webview&hl=en_US
  • Android 電量分分鐘鐘不夠用,該如何優化?
    電量消耗的計算與統計是一件麻煩而且矛盾的事情,記錄電量消耗本身也是一個費電量的事情,隨著Android的性能要求越來越高,電量的優化,也顯得格外重要,一個耗電的應用,用戶肯定會毫不猶豫的進行卸載,所以本篇博客,我們一起來學習
  • 安卓開源軟體商店,還可以換國內源;安卓webview更新導致眾多app閃退​
    AOSP畢竟是開源的,安卓也是有各種開源軟體,也有一個開源軟體應用商店:f-droid1、自己常用的一些開源軟體,比如termux,已經不再谷歌應用市場更新了,最新版在f-droid。telegram也是開源的,也可以在這裡更新。之前推薦過的rclone安卓gui版——無需root掛載各類網盤到手機,也是開源的。
  • 來點乾貨 | Android 常見內存洩漏與優化(二)
    在昨天的《Android 內存洩漏問題多多,怎麼優化?》Dalvik虛擬機支持.dex(即"Dalvik Executable")格式的Java應用程式的運行,.dex是專為Dalvik設計的一種壓縮格式,它是在.class字節碼文件的基礎上經過DEX工具壓縮、優化後得到的,適用於內存和處理器速度有限的系統。
  • Android Studio 3.5: 穩步推進 Project Marble 計劃
    該計劃圍繞系統健康、特性優化和錯誤修復三大核心領域,力圖在 Android Studio 與 Android 模擬器中構建功能強大且穩定的基礎特性與流。我們會在 Project Marble 計劃中直接整合收到的開發者反饋,歡迎大家繼續向我們提交反饋,分享您的想法與感受。為了提升 Android Studio 的系統健康,我們首先開發了一套全新的基礎架構和內部儀錶盤,以便更好地監測系統性能問題。
  • 在 Android 模擬器上運行 ARM 應用
    新的系統映像在執行 ARM 二進位文件方面取得了顯著的性能提升。此前,依賴 ARM 庫且無法構建 x86 版本應用的開發者只能使用完整的 ARM 模擬系統映像 (其速度遠低於在 x86 設備上運行 x86 系統映像) 或者實體機。Android 11 系統映像能夠在不影響整個系統的前提下,直接將 ARM 指令轉換成 x86 指令。
  • Unity高級知識點總結:性能優化與圖形渲染進階
    A.性能優化相關知識一、綜合優化1、降低屏幕解析度尤其是在android平臺對性能提升很大。可以有效緩解gpu的壓力。
  • 2018 前端性能優化清單
    RAIL,一個以用戶為中心的性能模型會為你提供健壯的目標。為了讓頁面達到小於 100ms 的響應,頁面必須要在在每小於 50ms 前將控制返回到主線程。預計輸入延遲時間會告訴我們,如果我們能達到這個門檻,在理想情況下,它應該低於 50ms。對於像動畫這樣的高壓點,最好不要在你能做到的地方做任何事,也不要做你不能做到的事。
  • 高盧雞與「黑豹」不得不說三兩事
    首先是「黑豹」坦克的主要性能優於盟軍當時裝備的絕大部分坦克,只有美軍剛剛投入使用的M26「潘興」坦克可以與之相提並論。法軍當時主要裝備的M4A2系列完全無法和「黑豹」正面抗衡。除了性能以外,「黑豹」是德軍在1943年才投入使用的新型坦克,1944年西線二線戰場開闢後,德軍精銳的法國西部和盟軍展開了困獸之鬥,其中有大量的「黑豹」坦克參戰,這些「黑豹」坦克有相當數量都是因為輕傷、機械故障、油料耗盡而被拋棄,這些廢棄坦克本身就具備很高的回收價值。基於這些原因,法國人從戰場上拖回來了一百餘量「黑豹」以及大量殘骸和可用零件。
  • 了解一下,Android 10中的ART虛擬機(2)
    在C++開發中,這個工作是由程式設計師來主動完成的。但對jvm來說,連結是由jvm來完成的。其目標都是一樣,把你代碼裡調用的符號和真實的地方對應起來。再有,對協程的重新認識。java裡的協程我最早在2010年左右就曾經接觸過,但作為一個C++開發者,我對線程的認識更熟悉,認為OS裡的線程調度是經過千錘百鍊的,而協程最終還是要轉換成對線程的使用,自己搞個調度器實在是🤮。
  • Android 約束布局(ConstraintLayout)詳解
    如果有缺少的約束(或其他的一些優化問題),編輯器會在右上角顯示警告提示(編譯器不會錯誤):(正常情況) (提示有 5 處可修復和優化的地方)轉換布局如果要將現有布局轉換為約束布局,只需按照下列步驟操作:在 Android Studio 中打開布局,然後單擊編輯器窗口底部的 Design 選項卡。
  • Android O 適配詳細指南
    公告http://bbs.360.cn/thread-15374485-1-1.html應用寶公告http://wiki.open.qq.com/wiki/Android_P%E7%89%88%E6%9C%AC%E9%80%82%E9%85%8D%E5%85%AC%E5%91%8A我們都知道每次 Android 版本的更新都會新增一大波優化功能
  • 歡迎加入 Android Q 測試版計劃!
    您可以開始對舊版應用進行最終的兼容性測試,並優化目前使用 API 或功能的任何新代碼。此外,從 Beta 4 開始,您將可以面向運行正式 API 級別的 Android Q 系統的設備 (例如選擇加入 Android Beta 版計劃的消費類設備) 發布應用。
  • Android 模擬器學習
    請記住,如果使用嵌套虛擬化,您將看到性能降低。Available images.us-docker.pkg.dev/android-emulator-268719/images/28-playstore-x64:30.1.2us-docker.pkg.dev/android-emulator-268719/images/28-playstore-x64-no-metrics:30.1.2us-docker.pkg.dev/android-emulator