1. 程式人生 > >解決UIWebView 前進、後退重新整理的坑

解決UIWebView 前進、後退重新整理的坑

    最近的專案在做瀏覽器,一開始我是拒絕的,瀏覽器市場絕對是個大坑, 這個坑裡的幾個玩家都是浸淫多年的大廠 or 大廠的私生子等等。而且我們的瀏覽器還相當的有“特色”,沒有位址列還收費、沒有位址列還收費、沒有位址列還收費。重要的事情說三遍。      前戲說完了,開始正題;UIWebView很弱,而且前進後退的時候還會reload,像瀏覽今日頭條 or taobao 時,從某個條目的detail頁面返回列表時會重新重新整理並回到列表頁面的頭部,使用者體驗極差,BOSS屌了好幾次,為什麼人家UC做的到。。      iOS8之後放出的WKWebView倒是不錯,效能各方面都和Safari差不多,也沒有這些reload的問題。不過WK的網路請求不能通過NSURLProtocol攔截,經過同事“很不細緻的調研”發現WK的網路請求是跨程序,而我們需要劫持流量進行計費,WK註定用不了。研究了下UCWeb,用的也是UIWebView。所以我選擇相信WK的網路請求攔截解決不了。。。        UIWebView的體系結構[ http://blog.csdn.net/hursing/article/details/8771847 ] 這哥們分析的很詳細,只是他沒告訴我怎麼解決前進後退不重新整理的問題,而且我把用”UIWebView  goBack” 關鍵字在 stack overflow上搜索出來的所有帖子都看了一遍,也找不到有用的內容來。經過仔細的測試,發現可以後退而不重新整理頁面的瀏覽器有:UCWeb、 QQ瀏覽器、百度瀏覽器、 搜狗瀏覽器;後退會reload的瀏覽器:Chrome、Opera、獵豹、海豚、極速瀏覽器、傲雲瀏覽器、手機百度。      開始我是毫無頭緒的,由於之前研究過UC在播放視訊時,是用JS劫持video物件和load、play呼叫,然後傳遞video url到OC Native,再彈出Native的VideoView來播放視訊,對於某些只有廣告連結或者收費視訊或者APP專享的視訊,懷疑UC是有一套後端視訊URL的聚合系統,可能通過ref url在後端查詢真實的視訊url來播放。不可否認UC在視訊上做的非常的屌。有這個先入為主的慣性思維,我一開始愚蠢的懷疑UC也是用JS解決goBack NOT reload的,為了驗證這個想法,我用MobileSubstrate注入動態庫[
http://blog.jobbole.com/58856/
 ]到UC的程序空間裡, Method Swizzling [ http://blog.csdn.net/yiyaaixuexi/article/details/9374411 ] UIWebView的 stringByEvaluatingJavaScriptFromString函式並直接返回,證明並非如此。      接下來也懷疑了NSURLCache和NSURLProtocol做了快取的影響,Hook initWithMemoryCapacity函式強制設定cache大小為0,同時在Hook UIWebView的loadRequest、goBack、goForward都去removeAllCachedResponses,對返回也是沒有影響。
驗證NSURLProtocol也是同樣Hook registerClass和setProperty 函式,全部讓其失效。然而也是並無卵用,      遇到死衚衕的時候,”Read The Fuxking Source Code” 就像一盞明燈一樣指引著我。。。雖然iOS的UIWebView沒有開源,但是Mac的有。     UIWebView包含了WebBrowserView,WebBrowserView中還包含了一個無比重要的WebView物件,WebView暴露了很多有用的介面,一個很重要的物件WebBackForwardList,通過閱讀WebCore的程式碼;WebBackForwardList實際上是BackForwardList類的跨平臺Wrapper類,BackForwardList就是裝有HistoryItem的前進後退佇列,同樣WebHistoryItem也是HistoryItem的Wrapper類, 而實現瀏覽器前進後退不重新整理的就是PageCache物件,PageCache是個單例,當點選一條連結跳轉時,當前頁面會被加入到gloablePageCache當中,返回時就直接從pageCache中拿出來顯示。

    WebBackForwardList有pageCacheSize和setPageCacheSize函式,讀出UCWeb和我們自己程式的pageCacheSize對比,UC是5,我們的是0,說明了UC開啟了pageCache,而我們沒有。然而,掉用setPageCacheSize 再讀出來還是0,同時劫持UC的setPageCacheSize函式,發現UC根本沒有呼叫[WebBackForwardList setPageCacheSize]。

      回到WebView類,PageCache有一個setMaxSize方法,在WebView的_setCacheModel中被呼叫,_setCacheModel會根據0-2三種不同的cache模式和實際記憶體的大小設定各種快取的大小,其中也包含了PageCache,實測證明在iPhone6 iOS8和iPhone5s iOS9.1上,cacheModel = WebCacheModelPrimaryWebBrowser時,可將pageCacheSize 改為3,解決前進\後退不重新整理。