騰訊X5內核 WebView 實踐總結

本篇文章是基於 騰訊X5內核 WebView 實踐的總結篇,較上篇文章更爲完整,具體。javascript

onPageFinished() 回調時機

經過 WebView 的回調函數,分析 onPageFinished() 回調時機前端

加載某個網址的Android端回調監測以下:java

shouldOverrideUrlLoading   time: 1519274808392

    onPageStarted: time: 1519274808561 // 169ms

    onPageFinished   time: 1519274809735 // 1174ms
    onReadableCallback: false

    shouldOverrideUrlLoading   time: 1519274811067 --url :shanbay.native.app://document/ready
    onReadableCallback: true

    onPageFinished   time: 1519274817879 // 9318ms
    onReadableCallback: true
複製代碼

據上數據分析: 第一次onPageFinished()回調觸發是在 1174ms (較onPageStarted()方法) 第二次onPageFinished()回調觸發是在 9318msgit

經過 chrome://inspect監測的資源加載時序 github

chrome://inspect

Network 面板突出顯示兩種事件:DOMContentLoadedloadweb

解析頁面的初始標記時會觸發 DOMContentLoaded。 此事件將在Network 面板上的兩個地方顯示:chrome

  1. Overview 窗格中的藍色豎線表示事件。
  2. Summary 窗格中,您能夠看到事件的確切時間。

頁面徹底加載時將觸發 load。此事件顯示在三個地方:緩存

  1. Overview 窗格中的紅色豎線表示事件。
  2. Requests Table 中的紅色豎線也表示事件。
  3. 在 Summary 窗格中,您能夠看到事件的確切時間。

分析上圖:bash

  1. DOMContentLoadedload 事件觸發時機與Android端的回調觸發時機不一致。
  2. 第一次onPageFinished()方法的調用和 document 類型文件加載完成時間相近,且通過屢次測試是在該文件加載完成後調用。
  3. 第二次onPageFinished()方法回調時間和load時間相近。

初步總結:網絡

  1. 第一次onPageFinished()方法是在document類型文件加載完成後調用的。
  2. 第二次onPageFinished()方法是在load完成時回調。
  3. 經過仔細查看shouldOverrideUrlLoading和onPageStarted方法時間差以及 圖中 Overview 欄,會發現加載網頁不是第一時間去請求數據的。因此 onPageStarted()方法較觸發是有必定的延遲時間。

ready 替換 onPageFinished 實現

據上分析的結果咱們會發現,onPageFinished()方法會調用屢次,因此,若是咱們將業務邏輯放到該方法中執行,若是不作控制,勢必會出現一些問題。固然,因爲網頁類型的多樣性,即便作了控制,依然會在特定的頁面出現問題。

那麼咱們如何擺脫對onPageFinished()的依賴呢?

網頁的加載情況,前端確定會有生命週期的感知,那麼咱們爲何不依賴前端的通知來觸發Native邏輯呢?

經過上述的思考,Native的事件觸發徹底交給前端去主動調取,而不是經過不靠譜的WebView回調。在前端的$.ready()方法中去通知移動端開始執行業務邏輯。

而且這種方式在時序性能方面有很大提高,比第二次onPageFinished()觸發時機早不少(在較爲複雜的頁面相差更大)

單頁應用

上面咱們經過 ready() 的主動通知,實現了 onPageFinished() 方法中業務邏輯的優化。

可是,在單頁應用的網頁中,$.ready() 只在主頁面渲染完成時觸發一次,在子頁面並不會觸發,並且,WebViewshouldOverrideUrlLoading()onPageStarted() 方法都不會回調。在一些單應用網頁會觸發 onPageFinished() 方法,它去請求了新的資源,因此咱們感知到了回調。而個別網頁並無去請求新的資源,直接對資源進行了替換,這種狀況,咱們就感知不到 onPageFinished() 的回調。

固然,若是開發本身的頁面就不存在這些多狀況的處理,能夠協商解決方案。

本文的主要實現是基於第三方網頁作的功能擴展,因此須要考慮這些兼容性問題。

給出不成熟的參考方案:

  1. 前端 url 變化監聽,通知移動端頁面變化。
  2. onPageFinished() 方法中再去作一個保底操做,損失一部分性能換取用戶的響應速度。

Js 注入時機以及時序控制

網絡上的廣泛作法是在 onPageFinished() 中注入 Js 腳本。

這種作法存在一些問題:

  • 可能會注入屢次。

  • onPageFinished()第二次調用時機很遲,在複雜的頁面性能損失很大。

  • 若是注入太多,會影響頁面的體驗。

因爲項目注入的腳本行數達到 1W+,因此咱們須要對時序作一些優化。保證調用時咱們已經完成了注入。

這裏咱們主要注入生成一個 script 標籤。

webView.loadUrl("javascript:(function() {" +
        "var scriptElement = document.getElementById('readability-script');" +
        "var parent = document.getElementsByTagName('body').item(0);" +
        "if(parent && !scriptElement) {" +
        "var script = document.createElement('script');" +
        "script.type = 'text/javascript';" +
        "script.id = 'readability-script';" +
         // Tell the browser to BASE64-decode the string into your script !!!
        "script.innerHTML = window.atob('" + mAssetsScript + "');" +
        "parent.appendChild(script);}" +
        "})()");
複製代碼

經過控制標籤的惟一性來防止注入屢次; 在頁面初始化前完成本地 js 腳本的文件讀取;不斷嘗試注入直到能夠注入爲止。

onProgressChanged() 回調中,不斷的嘗試讀取節點注入腳本。

經過最開始對 onPageFinished() 的分析。是否能夠嘗試在第一次回調時開始注入腳本。可是,不能保證每一個網頁都會回調兩次onPageFinished()

一般狀況下,CSS不會阻塞HTML的解析,但若是CSS後面有JS,則會阻塞JS的執行直到CSS加載完成(即使JS是內聯的腳本),從而間接阻塞HTML的解析。

資源加載回調

在研究WebView加載時序時發現了這個資源加載的回調onLoadResource()。這裏簡單介紹下,針對這個回調,能夠作的事情不少。

在加載頁面資源時會調用,每個資源(好比圖片)的加載都會調用一次。

webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean onLoadResource(WebView view, String url) {
      }
  });
複製代碼

能夠實現預加載及手動緩存的功能。優化用戶體驗而且減小屢次訪問形成的流量浪費。

調試

打造最溫馨的 WebView 調試環境

相關文章
相關標籤/搜索