iOS之深入解析WKWebView加载的生命周期与代理方法
生活随笔
收集整理的這篇文章主要介紹了
iOS之深入解析WKWebView加载的生命周期与代理方法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、前言
- 從 WebView 開始加載一條請求,到頁面完整呈現這一過程發生了什么?無論是做 WebView 性能優化還是異常問題監控與排查,都離不開對WKWebView加載的生命周期與代理方法的剖析。
- 在 WebKit 源碼的調試基礎之上, 結合 iOS 端 WKWebView 的 WKNavigationDelegate 代理方法,站在移動端的視角深入分析 WKWebView 網絡請求加載的生命周期流程。
- WebKit 源碼的調試,請參考我之前的博客:iOS之深入解析WKWebView的WebKit源碼調試與分析。
二、 WebKit 加載框架
- 在iOS之深入解析WKWebView的WebKit源碼調試與分析驗證了 WebKit 三大進程工作模型,并簡述了三大進程的主要職責。UIProcess、WebContent、NetworkProces 三大進程間通信關系圖如下:
- UIProcess、WebContent、NetworkProces 進程關系分析:
-
- NetworkProcess進程:主要負責網絡請求加載,所有的網頁共享這一進程。與原生網絡請求開發一致,NetworkProcess 也是通過封裝的 NSURLSession 發起并管理網絡請求的。但不同的是,這一過程中有較多的網絡進度的回調工作以及各類網絡協議管理,比如資源緩存協議、HSTS 協議、cookie 管理協議等。
-
- WebContent進程:主要負責頁面資源的管理,包含前進后退歷史,pageCache,頁面資源的解析、渲染。并把該進程中的各類事件通過代理方式通知給 UIProcess。
-
- UIProcess進程:主要負責與 WebContent 進行交互,與 APP 在同一進程中,可以進行 WebView 的功能配置,并接收來自 WebContent 進程的各類消息,配合業務代碼執行任務的決策,例如是否發起請求,是否接受響應等。
三、WebKit 加載流程
- 使用如下方法,從 UIProcess 層通過 loadReqeust 方法發起頁面加載請求(此處 request 只能是 get 請求,如果配置為 post 請求,WebKit 內核基于性能考慮,在跨進程傳輸時,會將 body 數據丟棄,導致異常):
- 通過跟蹤 WebKit 源碼,我們提取核心步驟如下:
-
- UIProcess 中的 loadRequest 首先會觸發 NetworkProcess 進程創建,然后通過進程間通信的方式將 request 發送給 NetworkProcess 進程進行 preconnect 預鏈接操作,通過網絡三次握手建立 TCP 鏈接,以便加快后續網絡資源請求速度。
-
- UIProcess 通過進程間通信的方式將 request 發送給 WebContent 進程,WebContent 進程創建 DocumentLoader 加載器加載網絡請求,并取消上個頁面的所有還在加載的請求,然后通過字典綁定當前頁面ID與創建好的 NetworkProcss 進程(便于服務端數據返回時,查找數據回填所對應的頁面),最終將請求交付給 NetworkProcess 中的 NSURLSession 進行處理。
-
- NetworkProcess 通過 NSURLSession 復用之前 preconnect 預鏈接,繼續進行網絡加載,此時等待網絡請求返回,網絡層會繼續將數據通過進程間通信方式傳輸給 WebContent 進程進行處理,開始流式進行數據解析,一邊接收一邊處理,進行詞法分析、語法分析,并在這一過程中加載解析出來的 js、css、圖片、字體等子資源,最終動態的生成(DOM 樹與 CSSOM 樹合成)渲染樹,在這一過程中,每次接受到新數據導致渲染樹有變更后,就會觸發一次 checkAndDispatchDidReachVisuallyNonEmptyState 方法,檢查當前頁面是否達到上屏狀態,若達到上屏狀態就進行上屏渲染。
- 達到上屏狀態的條件如下:
-
- 如果返回的 data 是普通文本文字,或返回的數據中包含普通文本文字,那只需要達到非空 200 字節即可以觸發上屏渲染;
-
- 如果返回的 data 是圖片資源類,則判斷像素大小 > 32*32,即可觸發上屏渲染;
-
- 如果不滿足以上條件,對于主文檔,判斷后面是否繼續接收數據,如果不繼續,則觸發上屏渲染;如后續還有數據,則循環上述流程直至觸發上屏。渲染完成,整個加載過程結束。
- WebKit 加載流程如下:
四、WebKit 加載生命周期代理方法
① WKNavigationDelegate 方法
@protocol WKNavigationDelegate <NSObject>@optional// 請求之前,決定是否要跳轉:用戶點擊網頁上的鏈接,需要打開新頁面時,將先調用這個方法。- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler; // 頁面開始加載時調用- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 接收到響應數據后,決定是否跳轉- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler; // 主機地址被重定向時調用- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 當開始加載主文檔數據失敗時調用- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error; // 當內容開始返回時調用- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation; // 頁面加載完畢時調用- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation; // 當主文檔已committed時,如果發生錯誤將進行調用- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error; // 如果需要證書驗證,進行驗證,一般使用默認證書策略即可 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler; // 9.0才能使用,web內容處理中斷時會觸發,可針對該情況進行reload操作,可解決部分白屏問題 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0); @end② 深入理解 WKNavigationDelegate 方法
- WKNavigationDelegate 代理方法的調用流程如下:
- decidePolicyForNavigationAction 剖析
-
- 如上文的網頁加載流程,當 WebContent 即將創建 DocumentLoader 加載器時,會首先觸發 decidePolicyForNavigationAction 代理方法。如果我們選擇 cancel,那么瀏覽內核會完全忽略這一操作,后續也不再繼續執行其他操作,可以放心的使用 cancel 取消掉我們不想加載的主文檔請求,而無需擔憂任何異常。
-
- 但當選擇 alllow 后,會進入一個稍微復雜的邏輯判斷,內核代碼首先判斷該該鏈接是否是 universalLink 類型的鏈接,如果判斷是 universalLink 類型的鏈接,會嘗試去調起三方 app,如果能調起,則會 cancel 當前請求,否則才會走到正常的網絡加載邏輯(如果需要統計 universalLink 調起情況與或建設屏蔽能力,可以再仔細閱讀該處源碼)。
- didStartProvisionalNavigation 理解
-
- decidePolicyForNavigationAction 方法中選擇 allow 并且判斷為非 universalLink 鏈接后,會立即觸發 didStartProvisionalNavigation 方法,表示即將開始加載主文檔。這個方法看似只是對 decidePolicyForNavigationAction 方法的確認,但是值得思考的問題是方法名中的 Provisional 究竟是什么意思。
-
- 其實,頁面開始頁面加載后為了更好的區分加載的各階段,會將網絡加載的初始階段命名為臨時狀態,此時的頁面是不會記入歷史的,直到接收到首個數據包,才會對當前頁面進行 committed 提交,并觸發didCommitNavigation 方法通知 UIProcess 進程該事件,同時將網絡 data 提交給 WebContent 進行渲染樹生成。可由此引申出下一個問題,即 didFailProvisionalNavigation 與 didFailNavigation 的關系。
- didFailProvisionalNavigation 與 didFailNavigation 的分別在什么時候執行?它們之間有什么關系?
-
- 當 NetworkProcess 進程發生網絡錯誤時,錯誤首先由 NSURLSession 回調到 WebContent 層。
-
- WebContent 會判斷當前主文檔加載狀態,如果處于臨時態,則錯誤會回調給 didFailProvisionalNavigation 方法;如果處于提交態,則錯誤會回調給 didFailNavigation 方法。
- didFinishNavigation 究竟什么時候執行?與頁面上屏是否有關?
-
- 在上面的描述中,我們已經理解了 NetworkProcess 層也是使用 NSURLSession 加載主文檔的。當 NSURLSession 接收到 finish 事件時,會將該消息通過進程通信方式傳遞給 WebContent 進程,WebContent 進程再傳遞給 UIProcess 進程,直到被我們的代理方法響應。
-
- 因此 didFinishNavigation 在 NSURLSession 的網絡加載結束時就會觸發,但因為跨了兩次進程通信,因此對比網絡層,實際上是有一定的延遲的。與子資源加載和頁面上屏無時間先后關系。
五、Tips
- 一定要緊密結合三大進程去理解 WebKit 源碼,形成基于進程的知識體系。
- 可以直接修改源碼驗證猜想,例如在驗證觸發渲染條件時,可以在源碼中禁止網絡層 didfinish 事件執行,并自己構造數據返回,驗證各類上屏觸發條件。
總結
以上是生活随笔為你收集整理的iOS之深入解析WKWebView加载的生命周期与代理方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS之深入解析WKWebView的We
- 下一篇: HarmonyOS之常用组件WebVie