解决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,解决前进\后退不刷新。




启用UIWebView PageCache的Demo  :  https://github.com/wadahana/WebViewDemo

猜你喜欢

转载自blog.csdn.net/wadahana/article/details/50168643