前言
白屏问题iOS开发中最令人头疼的问题之一。WKWebView随iOS8 推出已有4年了,解决了很多UIWebView的问题,比如内存泄漏、加载速度慢、不太兼容iOS10, iOS11等。Apple Support iOS版本的市场分布情况,绝大部分的设备都是iOS8 以上,iOS开始强制使用HTTPS请求,但还有部分公司的APP支持iOS7和HTTP请求,要赶上时代的步伐啊!
WKWebView的特点
- 性能高,稳定性好,占用的内存比较小
- 支持JS交互
- 支持HTML5 新特性
- 可以添加进度条(然并卵,不好用,还是习惯第三方的)
- 支持内建手势
- 据说高达60fps的刷新频率(不卡)
HTTPS的特点
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
HTTPS协议的安全是有范围的,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。
SSL的专业证书需要购买,功能越强大的证书费用越高。SSL 证书通常需要绑定 固定IP,为服务器增加固定IP会增加一定费用。
相同网络环境下,HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电。此外,HTTPS协议还会影响缓存,增加数据开销和功耗。
-
HTTPS 连接服务器端资源占用高较高多,相同负载下会增加带宽和服务器投入成本;
在使用的时候还是有很多坑,这其中就有屡见不鲜的白屏问题。经过一些调试、google 发现不外乎这几个原因。网络通畅是前提哈,请用4G或WiFi网络!!!话不多说了,来一起看看都有哪些可能的问题。
一. URL网址无效或含有中文字符(入门级错误)
APP内展示URL的来源主要是后端返回或前端拼接,甚至前端hardcode,网址存在不确定性,可能是无效或含有中文字符。大部分浏览器是能打开带有中文字符的网络地址,但是iOS的内嵌网页加加载框架无论是UIWebView还是WKWebView,都不能打开带有中文字符的网络地址,需要先对地址字符串做UTF8转码。参考代码:
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
二. HTTP请求问题
在iOS9 中,系统将原http协议改成了默认https协议,使用 TLS1.2 SSL加密请求数据。可以通过升级支持HTTPS协议请求,也可以通过设置强制使用HTTP请求。在Info.plist中添加NSAppTransportSecurity类型Dictionary。在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES。部分第三方应用不支持HTTPS,需要在在info.list设置HTTP请求白名单,允许部分请求可以是HTTP。参考代码:
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 微信 URL Scheme 白名单-->
<string>wechat</string>
<string>weixin</string>
</array>
三. HTTPS 请求,未实现证书认证代理导致页面不加载的问题
如果是HTTPS 请求,需要在WKWebView 的 WKNavigationDelegate 中的一个代理方法 中实现获取服务器认证的逻辑,最后返回给服务端。 这个问题常常出现在客户端无法获得安全认证的时候(没有证书,或者是自建证书),比如说https://www.apple.com/cn 是默认的苹果中国的地址,但是 https://www.apple.com.cn 也是可以访问的(会自动跳转到 https://www.apple.com/cn ) ,只是在Safari 的安全认证中通不过,我们需要在代理方法中通过服务端给的验证方式创建一个凭证,然后继续申请访问。比如在Safari 浏览器中第一次访问时就会弹出对话框,点击继续后就可以继续访问。通过实现以下代理即可解决
func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)
{ // 判断服务器采用的验证方法
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if challenge.previousFailureCount == 0 {
// 如果没有错误的情况下 创建一个凭证,并使用证书
let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
completionHandler(.UseCredential, credential)
} else {
// 验证失败,取消本次验证
completionHandler(.CancelAuthenticationChallenge, nil)
}
} else {
completionHandler(.CancelAuthenticationChallenge, nil)
}
}
四. iOS 8.0 - iOS 8.2 偶尔白屏问题
这个是WKWebView 刚推出时的Bug,偶尔会出现白屏,如果想避免这种问题,只能通过版本号判断,小于8.2的系统暂时使用UIWebView。
五. 滚动组件嵌套,未能刷新页面
在UITableView或UICollectionView中嵌套WKWebView引起的,可以试着将UITableView或UICollectionView中替换为UIScrollview 。可能是因为页面滚动后没有正常的调用 WKWebView 的 _updateVisibleContentRects 方法刷新需要渲染的内容导致的。
六. 硬件内存不足、进程崩溃
在 UIWebView 上当内存占用太大的时候,App Process 会 crash;而在 WKWebView 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象。在 WKWebView 中加载下面的测试链接可以稳定重现白屏现象:
这个时候 WKWebView.URL 会变为 nil, 简单的 reload 刷新操作已经失效,对于一些长驻的H5页面影响比较大。参考解决方案:
1、借助 WKNavigtionDelegate
iOS 9以后 WKNavigtionDelegate 新增了一个回调函数:
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0));
当 WKWebView 总体内存占用过大,页面即将白屏的时候,系统会调用上面的回调函数,我们在该函数里执行[webView reload](这个时候 webView.URL 取值尚不为 nil)解决白屏问题。在一些高内存消耗的页面可能会频繁刷新当前页面,H5侧也要做相应的适配操作。
2、检测 webView.title 是否为空
并不是所有H5页面白屏的时候都会调用上面的回调函数,比如,最近遇到在一个高内存消耗的H5页面上 present 系统相机,拍照完毕后返回原来页面的时候出现白屏现象(拍照过程消耗了大量内存,导致内存紧张,WebContent Process 被系统挂起),但上面的回调函数并没有被调用。在WKWebView白屏的时候,另一种现象是 webView.titile 会被置空, 因此,可以在 viewWillAppear 的时候检测 webView.title 是否为空来 reload 页面。