美文网首页WebView & JS
WKWebView启动优化

WKWebView启动优化

作者: yadong | 来源:发表于2022-05-24 17:16 被阅读0次

    1.背景

    苹果提供了WKWebView控件,开发者只要简单的使用几个API就能够展示网页内容,这样对于开发者来说却是一把双刃剑。好处是在于开发便捷,提高了开发效率。坏处在于WKWebView对于开发者来说完全变成了一个黑盒。如果说我们想对WKWebView进行一些监控,或者是进行启动优化时,是否有方法可循呢?

    理解问题的本质才能更好的处理问题。所以需要分析WKWebView的启动到底在做什么事情?

    2. 分析

    我将webView启动分为三个阶段:wkwebView初始化阶段,浏览器网络资源请求阶段,浏览器渲染阶段。接下来我会逐一进行分析

    2.1 WKWebView初始化阶段

    从代码上来看,WKWebView的使用是非常简单的:

    WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];

    初一看确实貌似没什么问题,但是细想却又有点奇怪,为什么会觉得怪?

    那就是alloc初始化之后,是否意味着WKWebView已经初始化完成了呢?

    我通过以下三点进行了考虑:

        1. WKWebView是在另外一个进程上运行的

        2. WKWebView是NSObject的子类,通过alloc方法创建之后,实际上就是在我们APP的进程内存中创建了一个WKWebView的结构体而已

        3. 初始化完成状态应该可用状态,结合第一点考虑,那应该也要初始化一个WKWebView进程

    通过上面三点分析,可以得出结论alloc之后,并不意味着初始化完成

    那经历init方法后,是否意味着WKWebView已经初始化完成了呢?

    init构造方法确实还不太好从理论上分析,谁也不知道构造方法里面写入了什么内容,好在WebKit开源,那么就直接从源码上面分析吧

    通过源码分析函数走向为: initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration -> _initializeWithConfiguration:(WKWebViewConfiguration *)configuration;

    核心流程在第二个函数中。直接看看部分代码吧~

    从代码上来看,主要做了如下几件事情:

        1. 设置WKProcessPool

        2. 设置配置信息

        3. 初始化WKContentView并绑定

        4. 生成 WebPageProxy并赋值到configuration

        5. 设置窗口大小与展示模式

        6.添加一些通知

        7. 添加一些代理

    总的来看,都是对WKWebView这个对象进行操作,并未提及到进程初始化。所以可以得出结论,init方法之后,也不意味着初始化完成。那对于WKWebView操作还有两个方法:loadRequest: 和 addSubView:addSubView:首先排除,直接分析loadRequest:

    分析loadRequest:方法。通过符号断点抓取函数栈,先看看函数栈。

    源码分析WebPageProxy::loadRequest这个方法到底做了什么?

    看1368和1369两行代码,从字面意思描述的比较清楚了。先判断有没有进程,如果没有进程的话,就初始化一个进程~

    那先分析hasRunningProcess里面做了什么事情:

    主要就是通过一个m_hasRunningProcess的标识位,来判断当前的WKWebView有没有开启WKWebView的进程。

    接下来看看初始化进程lauchProcess方法里面做了什么吧~

    主要操作如下:

        1. 检查器重置

        2. 843行-844行。WKWebView进程(后面统称WK进程)移除关联的WKWebView与IPC通信。这一步其实很迷惑,既然是WKWebView中没有WK进程的话,那初始化进程的时候为什么会有移除关联的操作? 这里就引伸出来另一个概念:WK进程复用。也就是当我们APP关闭一个WKWebView之后,WK进程并没有立刻结束,而是会在后台保持一段时间。这其实也很好的说明了一个现象:第二次开启WKWebView要比第一次开启WKWebView初始化耗时更少

        3. 安全启动WK进程

        4. WK进程绑定当前的WKWebView以及建立IPC通信

        5. 注入一些信息

    走到这里也可以盖棺定论了,loadRequest:方法之后,才算初始化完成。那总结下WKWebView初始化都经历了那些事情吧,以一张图来展示

    2.2 浏览器网络资源请求阶段

    一个网页需要完整的在浏览器展示需要用到三个东西:

        1. HTML/SVG/XHTML,HTML字符串描述了一个页面的结构,浏览器会把HTML结构字符串解析转换DOM树形结构

        2. CSS,解析CSS会产生CSS规则树,它和DOM结构比较像

        3. Javascript脚本,等到Javascript 脚本文件加载后, 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree。

    而这些资源都是通过网络请求,或者从本地缓存中读取,只有拿到这些资源后,浏览器才能开始进行渲染的操作。简而言之,获取资源的速度决定了WKWebView启动的速度。

    那在什么时机才意味着网络资源请求全部完成呢?

    所以我们需要分析浏览器下载资源流程,主要流程为四步:

        1. 浏览器下载HTML,先从本地缓存进行查找,如果本地缓存没有,则发起网络请求

        2. 获取HTML后,浏览器解析HTML,然后一边构建DOM,一边下载解析到的CSS,JS资源,也会先从本地缓存进行查找,如果本地缓存没有,则发起网络请求

        3. 结合DOM与CSS生成的CSSOM,生产Render Tree。这一步是和第2步并发执行

        4. 浏览器根据Render Tree进行渲染。这一步也是并发执行

    用一张图来表示:

    在HTML解析完成,并下载全部的CSS,JS之后,才能够生成完全的Render Tree,此时的浏览器才能完整的渲染出网页内存。那么在此时,我们才认为网络资源请求已经完成

    3. 解决

    分析完流程,接下来的优化也就比较好解决了,解决方案按照下面四个方向进行就好

        1.那些事情可以提前做

        2. 那些事情可以一起做

        3. 那些事情可以不用做

        4. 那些事情可以减少做

    3.1 webView初始化阶段调优

        1.预初始化一个WKWebView (提前做)

        2. WKWebView复用池(减少做)

    流程图如下:

    需要注意的点:

        1. WKWebView入池后,需要loadRequest一个空页面,此时才能能算真正启动初始化流程

        2. WKWebView出池时,只有完成didFinish回调后,才能顺利出池。避免因为delegate而引起其他异常

        3. 复用的WKWebView的需要清除backForwardList

    3.2 网络请求阶段调优

        1. 网络资源缓存池构建(减少做)

        2. 资源预下载(提前做)

        3. HTML预解析(提前做)

        4. 并发进行webView初始化,HTML下载,资源表CSS,JS下载(一起做)

    需要做到上叙四点,需要解决一个壁垒。WKWebView的网络拦截以及WKWebView的资源管理。QMTBS的特点是:能够完整的拦截到WKWebView发起的网络请求,并且拦截时机是在访问浏览器缓存之前。通过这两个特点,就能很好的对WKWebView网络阶段进行控制和管理。通过一张简图来介绍QMTBS拦截时节:

    3.2.1 网络资源缓存池构建

    缓存分为两级:

        1. 强缓存:不会向服务器发起请求,直接从缓存中读取缓存

        2. 协商缓存:当强缓存失败后,携带缓存标识字段向服务器发起请求,由服务器根据缓存标识字段来决定是否使用缓存

    流程图如下:

    3.2.2 资源预下载与HTML预解析

    其实通过浏览器网络资源请求的分析,可以得到一个结论:要想知道HTML里面的资源,就得先下载HTML并解析。那除去这种方案,那还有其他方法可以知道HTML里面的资源呢?答案是:前端同学知道。那让前端同学在合适的时机以合适的方式告诉我们就好了

    合适的时机:APP启动60秒之后拉取,以及每10分钟轮询一次

    合适的方式:网页资源表

    资源表结构如下:

    资源分为三个等级:高等级:资源存在资源列表中,且PromptlyDown = 1。高等级资源会在拉取到资源表且为WIFI环境,则立刻下载资源

    中等级:资源存在资源列表中,且PromptlyDown = 0。中等级资源会在拉取到资源表且为WIFI环境且客户端网络有空闲时,则立刻下载资源

    低等级:资源只存在网页列表中。低等级资源只会在打开对应网页时,才会下载资源

    3.2.3 WebView启动流程调优

    在webView的整体启动流程中,webView初始化、HTML下载,CSS,JS下载三者是一个串行关系。由于HTML,CSS,JS的下载都不是UI操作,所以可在子线程中进行,那么对于这种串行的流程实际上没有完全利用手机性能,造成了性能浪费。通过上叙的优化后,我们可以对webView初始化,以及网络请求进行控制。那么我们就可以将webView初始化、HTML下载,CSS,JS下载三者调整为并发进行,使得性能尽可能的完全使用。

    先来看看浏览器本来的资源加载流程:

    改为并发之后为:

    3.3 整体流程

    4. 相关文章

    https://ming1016.github.io/2017/10/11/deeply-analyse-webkit/

    https://github.com/ljianshu/Blog/issues/51

    https://github.com/ljianshu/Blog/issues/25

    https://github.com/ljianshu/Blog/issues/23

    https://github.com/ljianshu/Blog/issues/24

    相关文章

      网友评论

        本文标题:WKWebView启动优化

        本文链接:https://www.haomeiwen.com/subject/bjajprtx.html