android性能優化(三)之Webview優化

閱讀本文大概需要 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內存泄露


 

進入公衆號,回覆“程序員“可以領取一份計算機技術電子書福利合集

歡迎轉發,關注公衆號 肖暉

每天幾分鐘,掌握一個硬核面試知識點

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章