UIWebView 的坑點

UIWebView 之痛

開發App的過程中,常常會遇到在App內部加載網頁,通常用UIWebView加載。而這個自iOS2.0開始使用的Web容器一直是開發的心病:加載速度慢,佔用內存多,優化困難。如果加載網頁多,還可能因爲過量佔用內存而給系統kill掉。各種優化的方法效果也不那麼明顯,常見的優化緩存方式:
1、儘量使用 GET 請求,iOS 系統 SDK 會自動幫你做緩存。你需要的僅僅是設置下內存緩存大小、磁盤緩存大小、以及緩存路徑。只要設置了這兩行代碼,基本就可滿足80%的緩存需求。


設置緩存.jpg


2、Web資源離線加載,熱更新資源,完成另外20%的緩存需求(Hybrid框架的Web部分)。
可是無數開發者嘗試自己做一個“簡陋而脆弱的”系統來實現網絡緩存的功能,效果往往是事倍功半 。

初識 WKWebView

UIWebView從 iOS2 就有,iOS8 以後,蘋果推出了新框架 WebKit,提供了替換 UIWebView 的組件 WKWebView。各種 UIWebView 的性能問題沒有了,速度更快了,佔用內存少了,體驗更好了,下面列舉一些其它的優勢:
1、在性能、穩定性、功能方面有很大提升(加載速度,內存的提升誰用誰知道)
2、更多的支持 HTML5 的特性
3、官方宣稱的高達60fps的滾動刷新率以及內置手勢
4、Safari 相同的 JavaScript 引擎
5、將 UIWebViewDelegate 與 UIWebView 拆分成了14類與3個協議,包含該更細節功能的實現。
相比之下,WKWebView 複雜得多,一些常用API如下:

容器相關


WKWebView.jpg

JavaScript 配置相關


JavaScript API.jpg

存儲相關(只支持iOS9以上)


存儲類型.jpg

存儲 API.jpg

頁面加載相關


WKWebView loadRequest.jpg

代理相關


WKNavigationDelegate.jpg

看完API以後,要掌握 WKWebView 並不難,難的是如何處理iOS版本碎片化兼容問題。

性能對比測試

都說提高多麼多麼大的性能,實測告訴你 WKWebView 的性能有多好,下面用實際項目做個對比測試:
UIWebView 首次加載 www.58.com 首頁,耗時 0.0154584ms,內存消耗 24.1 MB


UIWebView耗時.jpg

UIWebView內存消耗.jpg

WKWebView 首次加載 www.58.com 首頁,耗時 0.013875ms,內存消耗僅 6.4 MB


WKWebView耗時.jpg

WKWebView內存消耗.jpg

結論:加載耗時差別不大,WKWebView 的內存優化減少了幾乎4倍,更重要的是,無論 WKWebView 跳轉多少 Web 頁面都沒有內存泄漏了。WKWebView 使用和 Safari 相同的 Nitro JS 引擎性能,對HTML5性能也提升了4倍。

WKWebView 之坑

新技術的出現必然會或多或少的瑕疵,WKWebView 也不例外。

1、關於緩存

在 WKWebsiteDataStore 出現之前(iOS 9 中),WKWebView 是沒有緩存,也無從清理。WKWebView 是基於 WebKit 框架的,它會忽視先前使用的網絡存儲 NSURLCache, NSHTTPCookieStorage, NSCredentialStorage等,它也有自己的存儲空間用來存儲cookie和cache,其他的網絡類如NSURLConnection 是無法訪問到的。 同時WKWebView發起的資源請求也是不經過NSURLProtocol的,導致無法攔截或自定義新請求。
體驗過 WKWebView 的一定會遇到修改了H5頁面,APP打開時卻沒有即時更新的問題(實在是緩存得太好了),iOS 8的時候只能增加時間戳的方式解決這個問題(調試下使用,生產環境就只能要求前端修改Cache-Controll了),如下:


時間戳更新.jpg

iOS 9以後終於可以使用 WKWebsiteDataStore 來清理緩存。後來Google一下,又發現iOS 8可以通過清理 Library 目錄下的 Cookies 目錄來清除緩存,於是


清除WKWebView緩存.jpg

緩存清理的坑趟過了,喜大普奔。

在使用 UIWebVIew 的時候我們並不關注 Cookie,因爲在調用登錄接口的時候無論是AFNetworking,還是其他,登錄成功之後都會自動保存在
[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies 中,以後再使用也會自動去獲取(這裏有個 UIWebView 的坑:訪問的鏈接越多,如不處理Cookie,它會加載越來越多的無效 Cookie 導致內容急劇增大)。但 WKWebView 的存儲體系與 UIWebVIew 完全不一樣,只能手動給它添加 Cookie,如下:


WKWebView設置cookie.jpg

但即便如此,Cookie 還是會偶現丟失的問題,最終只好採用每次 Web 開始加載之時判斷 Cookie 是否存在,否則手動添加重新加載,如下:


WKWebView設置cookie.jpg

Cookie 獲取的坑趟過了,再次喜大普奔。

3、關於跨域

WebKit框架對跨域進行了安全性檢查限制,不允許跨域,比如從一個 HTTP 頁對 HTTPS 發起請求是無效的(有一個界面要跳到支付寶頁面去支付,死活沒反應)。而系統的 Safari ,iOS 10出現的 SFSafariViewController 都是支持跨域的,因此解決辦法如下:


跨域跳轉.jpg

對於自身域名,還是建議全站 HTTPS 化吧(大勢所趨)。

4、關於 JavaScript 交互

UIWebView 使用的 JavaScriptCore 框架,交互時爲 JavaScript 運行的上下文環境 JSContext 注入對象 Bridge;WKWebView 使用的 WebKit 框架,交互時爲 webkit.messageHandlers 注入對象,如下:


JavaScript注入.jpg

前端H5需要做判斷兩種不同注入方式帶來的不同調用方式:


js調用.jpg

5、關於 NSURLProtocol 攔截

WKWebView 基於 WebKit 框架,與 UIWebView 機制不同:加載過程中所有的請求都不經過 NSURLProtocol,換句話說就是 WKWebView 無法攔截響應數據 鑑於之前大部分 Hybrid 框架的離線預加載機制都依賴於攔截功能,這意味着廢掉很多程序猿們辛辛苦苦設計實現的 Hybrid 框架(內功盡失,感覺身體被掏空),再加上 WKWebView 自身的坑不少,因此很多團隊都不會輕易替換掉 UIWebView。擁抱變化吧,WKWebView 遲早會取代 UIWebView 成爲最佳 Web 容器(iOS 9帶來的 SFSafariViewController 更是武功全廢,啥都幹不了,只能乾瞪眼)。

那麼問題來了,如何設計新的 Hybrid 框架呢?此處出門左轉,點擊文章開頭進入公衆號歷史文章,查看《通用Web&Native交互協議設計方案》。

6、關於 POST 請求

簡書 http://www.jianshu.com/p/403853b63537 中有關於這個坑的具體描述,筆者這裏就不再做研究,這裏只說明怎麼趟過的坑:使用通用的 Web&Native 交互協議,爲 Web 提供 Native POST 請求的接口+回調 CallBack 即可,參見 關於 JavaScript 交互

7、關於本地 HTML 加載

當使用 loadRequest 來讀取本地 Documents 目錄的 HTML 文件時,WKWebView 是無法讀取成功的,只能通iOS 9的新接口加載


load loacl html.jpg

但是在iOS9以下的版本是沒提供這個便利的方法的,解決辦法:先將本地 HTML 文件的數據 copy 到 tmp 目錄中,然後再使用 loadRequest 來加載。但是如果在 HTML 中加入了其他資源文件,例如 js,css,image 等也必須一同 copy 到 tmp 中,這個是非常蛋疼的事情了。然而還有更蛋疼的事:iOS 8下還必須 copy 到 tmp 的 www 目錄下 WKWebView 才能讀取(Word天,心中千萬只草泥馬狂奔而過)。參見 http://stackoverflow.com/questions/24882834/wkwebview-not-loading-local-files-under-ios-8

8、關於捏合手勢

很多人都喜歡使用 UIWebView 的捏合手勢來進行放大和縮小,觀看 Web 內容,但 WKWebView 在手機上不支持,卻支持其他iOS設備(草泥馬再次狂奔而過)。


不支持捏合手勢.png

寫在最後

當時選擇 WKWebView 就是爲了提高性能,但是沒有想到遇到這麼多坑,真是我待 WKWebView 如初戀,WKWebView 虐我千百遍,興許還有許多未知的坑,歡迎大家留言補充。謝謝支持!


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