iOS之深入解析WKWebView加載的生命周期與代理方法

      網友投稿 2088 2025-04-01

      從 WebView 開始加載一條請求,到頁面完整呈現這一過程發生了什么?無論是做 WebView 性能優化還是異常問題監控與排查,都離不開對WKWebView加載的生命周期與代理方法的剖析。

      在 WebKit 源碼的調試基礎之上, 結合 iOS 端 WKWebView 的 WKNavigationDelegate 代理方法,站在移動端的視角深入分析 WKWebView 網絡請求加載的生命周期流程。

      WebKit 源碼的調試,請參考我之前的博客:iOS之深入解析WKWebView的WebKit源碼調試與分析。

      在iOS之深入解析WKWebView的WebKit源碼調試與分析驗證了 WebKit 三大進程工作模型,并簡述了三大進程的主要職責。UIProcess、WebContent、NetworkProces 三大進程間通信關系圖如下:

      UIProcess、WebContent、NetworkProces 進程關系分析:

      NetworkProcess進程:主要負責網絡請求加載,所有的網頁共享這一進程。與原生網絡請求開發一致,NetworkProcess 也是通過封裝的 NSURLSession 發起并管理網絡請求的。但不同的是,這一過程中有較多的網絡進度的回調工作以及各類網絡協議管理,比如資源緩存協議、HSTS 協議、cookie 管理協議等。

      WebContent進程:主要負責頁面資源的管理,包含前進后退歷史,pageCache,頁面資源的解析、渲染。并把該進程中的各類事件通過代理方式通知給 UIProcess。

      UIProcess進程:主要負責與 WebContent 進行交互,與 APP 在同一進程中,可以進行 WebView 的功能配置,并接收來自 WebContent 進程的各類消息,配合業務代碼執行任務的決策,例如是否發起請求,是否接受響應等。

      使用如下方法,從 UIProcess 層通過 loadReqeust 方法發起頁面加載請求(此處 request 只能是 get 請求,如果配置為 post 請求,WebKit 內核基于性能考慮,在跨進程傳輸時,會將 body 數據丟棄,導致異常):

      [self.webView loadRequest:request];

      1

      通過跟蹤 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 加載流程如下:

      @protocol WKNavigationDelegate @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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      iOS之深入解析WKWebView加載的生命周期與代理方法

      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 的網絡加載結束時就會觸發,但因為跨了兩次進程通信,因此對比網絡層,實際上是有一定的延遲的。與子資源加載和頁面上屏無時間先后關系。

      一定要緊密結合三大進程去理解 WebKit 源碼,形成基于進程的知識體系。

      可以直接修改源碼驗證猜想,例如在驗證觸發渲染條件時,可以在源碼中禁止網絡層 didfinish 事件執行,并自己構造數據返回,驗證各類上屏觸發條件。

      iOS 任務調度 網絡

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:WPS表格僅顯示公式不計算結果怎么辦(wps表格公式不顯示結果只顯示公式)
      下一篇:python3 API文檔和源碼下載
      相關文章
      亚洲色av性色在线观无码| 久久久久亚洲AV无码专区首JN | 亚洲av无码一区二区三区天堂古代| 国产成人亚洲影院在线观看| 亚洲精品国产综合久久久久紧| 亚洲人成7777影视在线观看| 亚洲美女中文字幕| 国产精品久久久亚洲| 亚洲乱色伦图片区小说| 亚洲成人网在线播放| 亚洲最大福利视频网站| 亚洲免费视频网站| 中国亚洲女人69内射少妇| 成人亚洲国产va天堂| 亚洲精品日韩专区silk| 亚洲av永久无码精品国产精品| 久久亚洲精品无码观看不卡| 亚洲国产aⅴ综合网| 久久久久久久久无码精品亚洲日韩| 久久亚洲国产成人影院| 亚洲伊人久久大香线蕉结合| 国产91在线|亚洲| 亚洲变态另类一区二区三区| 色欲aⅴ亚洲情无码AV蜜桃| 深夜国产福利99亚洲视频| AV在线亚洲男人的天堂| 国产精品亚洲成在人线| 亚洲色图在线播放| 亚洲国产精品午夜电影| 亚洲午夜精品久久久久久app| 亚洲国产精品无码久久九九大片 | 亚洲偷偷自拍高清| 亚洲国产欧美一区二区三区| 偷自拍亚洲视频在线观看99| 亚洲日本在线观看视频| 国产亚洲人成无码网在线观看 | 亚洲资源在线观看| 亚洲Av无码一区二区二三区| 亚洲乱色伦图片区小说| 亚洲一区二区视频在线观看| 亚洲AV无码码潮喷在线观看|