閱讀本文大概需要 4 分鐘。
這篇把Webview的優化單獨拎出來,是因爲Webview與其他普通view來說有自身獨特的特點。
在做一些native+H5應用開發時,Webview的性能問題一直是關注的重點。所謂Webview性能問題,就是從打開Webview頁面開始到可以和用戶交互,這期間花費的時間相對於原生頁面來說過長,從直觀上來講,用戶等待的時間變長了!
而用戶的耐心是有限的,如果幾秒鐘之內頁面仍是白屏,很有可能就會關閉此頁面,那麼頁面功能再酷炫,再強大也沒用。而通常情況下,公司的活動頁往往都用H5來實現,因此如果頁面展現速度過慢的話,還會造成公司損失。
因此,做好Webview優化是很有必要的。
一. 分析
爲什麼擁有Webview的H5頁面打開這麼慢,是因爲它通常會經歷以下幾個階段:
1)Webview初始化。
2)到達新的頁面,網絡連接,從服務器下載html,css,js,頁面白屏。
3)頁面基本框架出現,js請求頁面數據,頁面處於loading狀態。
4)出現所需的數據,完成整個頁面的渲染,用戶可交互。
可以用下面這張圖來表示:
很明顯,相對於原生應用只需要從後臺拉取數據進行渲染來說,Webview多了初始化,拉取整個頁面資源這2個步驟,而且他們的順序是串行的。
即必須在完成初始化後才能開始建立網絡連接(因爲Webview相當於瀏覽器客戶端,我們在PC上也是必須先打開chrome瀏覽器才能再輸入網址打開頁面),拉取html,css,js等資源,而只有拉取到js之後,js才能發起ajax網絡請求,獲取需要展現的動態數據。
而初始化,建立網絡連接,拉取數據恰恰都是比較耗時的操作,這就是爲什麼我們從直觀上來講感覺速度太慢的原因。
知道了原因後,我們就可以針對各個階段來逐個優化。
二. Webview提前初始化
我們知道每個頁面在打開時都會調用setContentView()方法 -> inflate() -> createViewFromTag(),也就是說都會調用view的構造函數,webview也不例外,但是不同的是webview的首次構造耗時比較長。
可以通過簡單的代碼來對比一下Webview首次初始化和第二次所花費的時間:
private void testWebViewInitUsedTime(){
long p = System.currentTimeMillis();
WebView mWebView = new WebView(this);
long n = System.currentTimeMillis();
Log.i("Info", "testWebViewFirstInit use time:" + (n-p));
}
testWebViewInitUsedTime();
testWebViewInitUsedTime();
//測試環境 Android 7.0 三星S7
testWebViewFirstInit use time:182
testWebViewFirstInit use time:4
可見第二次所用的時間遠遠小於第一次,這是爲什麼呢?
通過閱讀源碼,我們會發現Webview所有的邏輯處理都是通過WebViewProvider來實現的,因爲它要加載Webview內核,這是一個重量級的操作,內核是以apk的形式存在。而內核加載後在同一頁面是共享的,因此後續的初始化時間就很少了。
static WebViewFactoryProvider getProvider() {
...
Class<WebViewFactoryProvider> providerClass = getProviderClass();
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
.newInstance(new WebViewDelegate());
return sProviderInstance;
}
private static Class<WebViewFactoryProvider> getProviderClass() {
...
loadNativeLibrary();
}
private static int loadNativeLibrary() {
...
String[] args = getWebViewNativeLibraryPaths();
int result = nativeLoadWithRelroFile(args[0] /* path32 */,
args[1] /* path64 */,
CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
}
既然如此,我們可以在App生成一個全局webview,並且在啓動時初始化,這樣在後面使用時通過動態獲取這個全局Webview,然後添加到rootview中,這樣就可以進行復用從而減少初始化的時間。
不過需要注意的是,如果在不同頁面使用Webview時使用不同的設置,就需要動態維護,而且在不同的頁面跳轉前要做好清理工作。
三.H5頁面拉取優化
有了Webview,就要開始拉取H5頁面了。針對這一步,我們可以把html,css,js,image等資源預置在客戶端本地,並和服務端協商好前端的版本控制和增量更新策略,如此一來Webview就可以先快速加載本地緩存頁面資源,剩下的就只需要拉取那些需要更新的增量資源即可。
而針對這些需要拉取的增量資源,可以對它們進行webpack+gzip數據壓縮和CDN加速處理,以提升拉取速度。並且在建立網絡連接時,可以讓前端請求的域名和客戶端API接口域名一致,以減少DNS解析時間。
最後,對於H5頁面來說,圖片資源的拉取是最爲耗時的,一個比較好的解決方案就是先加載並展示非圖片內容,延遲這些圖片的加載,以提升用戶體驗。
WebView有一個setBlockNetworkImage(boolean)方法,該方法的作用是是否屏蔽圖片的加載。可以利用這個方法來實現圖片的延遲加載:在onPageStarted時屏蔽圖片加載,在onPageFinished時開啓圖片加載。
四.H5動態數據拉取並行
正常的順序是在html,css,js拉取下來之後,纔開始由js發起前端的ajax請求,獲取到數據後纔開始進行填充。
其實我們可以把前端的ajax請求提前到和頁面加載同時進行,由客戶端請求數據,等到H5加載完畢,直接向客戶端索要即可,如此一來,便縮短了總體的頁面加載時間。
五.總結
除了上述提到的點,其實還有很多其他的方案,比如可以通過靜態直出,直接下發首屏html的方式加速首屏的展現,也可以通過智能預取的方式提前進行網絡請求數據到本地,甚至有些重度依賴H5的App利用預先準備好數據的Webview池來達到加速的目的。
所以,Webview優化是一個長期的、綜合性的工作,有許多細節可以考慮,需要根據業務需求來不斷升級優化策略。
一切從android的handler說起(一)之message
一切從android的handler說起(二)之threadLocal
一切從android的handler說起(三)之UI線程不卡頓
一切從android的handler說起(四)之postDelay原理
一切從android的handler說起(五)之觸摸事件模型
一切從android的handler說起(六)之生命週期來源
一切從android的handler說起(七)之Handler內存泄露
進入公衆號,回覆“程序員“可以領取一份計算機技術電子書福利合集
歡迎轉發,關注公衆號 肖暉
每天幾分鐘,掌握一個硬核面試知識點