本篇文章是基於 騰訊X5內核 WebView 實踐的總結篇,較上篇文章更爲完整,具體。javascript
經過 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
Network 面板突出顯示兩種事件:DOMContentLoaded
和 load
web
解析頁面的初始標記時會觸發 DOMContentLoaded
。 此事件將在Network 面板上的兩個地方顯示:chrome
頁面徹底加載時將觸發 load。此事件顯示在三個地方:緩存
分析上圖:bash
DOMContentLoaded
和 load
事件觸發時機與Android
端的回調觸發時機不一致。onPageFinished()
方法的調用和 document
類型文件加載完成時間相近,且通過屢次測試是在該文件加載完成後調用。onPageFinished()
方法回調時間和load
時間相近。初步總結:網絡
onPageFinished()
方法是在document
類型文件加載完成後調用的。onPageFinished()
方法是在load
完成時回調。shouldOverrideUrlLoading
和onPageStarted
方法時間差以及 圖中 Overview 欄,會發現加載網頁不是第一時間去請求數據的。因此 onPageStarted()
方法較觸發是有必定的延遲時間。據上分析的結果咱們會發現,onPageFinished()
方法會調用屢次,因此,若是咱們將業務邏輯放到該方法中執行,若是不作控制,勢必會出現一些問題。固然,因爲網頁類型的多樣性,即便作了控制,依然會在特定的頁面出現問題。
那麼咱們如何擺脫對onPageFinished()
的依賴呢?
網頁的加載情況,前端確定會有生命週期的感知,那麼咱們爲何不依賴前端的通知來觸發Native
邏輯呢?
經過上述的思考,Native
的事件觸發徹底交給前端去主動調取,而不是經過不靠譜的WebView
回調。在前端的$.ready()
方法中去通知移動端開始執行業務邏輯。
而且這種方式在時序性能方面有很大提高,比第二次onPageFinished()
觸發時機早不少(在較爲複雜的頁面相差更大)
上面咱們經過 ready()
的主動通知,實現了 onPageFinished()
方法中業務邏輯的優化。
可是,在單頁應用的網頁中,$.ready()
只在主頁面渲染完成時觸發一次,在子頁面並不會觸發,並且,WebView
的 shouldOverrideUrlLoading()
及 onPageStarted()
方法都不會回調。在一些單應用網頁會觸發 onPageFinished()
方法,它去請求了新的資源,因此咱們感知到了回調。而個別網頁並無去請求新的資源,直接對資源進行了替換,這種狀況,咱們就感知不到 onPageFinished()
的回調。
固然,若是開發本身的頁面就不存在這些多狀況的處理,能夠協商解決方案。
本文的主要實現是基於第三方網頁作的功能擴展,因此須要考慮這些兼容性問題。
給出不成熟的參考方案:
url
變化監聽,通知移動端頁面變化。onPageFinished()
方法中再去作一個保底操做,損失一部分性能換取用戶的響應速度。網絡上的廣泛作法是在 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) {
}
});
複製代碼
能夠實現預加載及手動緩存的功能。優化用戶體驗而且減小屢次訪問形成的流量浪費。