写在最后
对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。
![
文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
按照这个标准,一旦存在某个图片一直处在加载中,但页面框架的其他内容均已处理完毕,onPageFinished 回调也会等待图片加载完成才回调,与实际上的 “页面内容可消费” 时间点存在差异。
3. 指标设计方案
结合上述分析,可以确定:
- 最准确的页面加载完成时机来自前端
- 最准确的 WebView 初始化时机来自客户端
因此,完善的耗时测量需由客户端和前端协同完成。
(1)前端侧
前端自行完成结束时间点的设置,并从客户端获取 WebView 初始化时间点,统计上报打开耗时。
- 前端通过手动埋点或监听 DOM 节点数变更,获取加载完成时间点。
- 前端统计时调用客户端提供 JSAPI,获取以 WebView 初始化时间点作为起点的耗时。
- 并由前端完成加载耗时的计算和统计上报。
(2)客户端侧
作为一个补充方案,客户端可以通过 JavaScript 注入获取上述 W3C Performance Timing 中的 domInteractive 时间点,作为结束时间点。
- 前端
domInteractive
时,已完成所有页面展示必需资源的请求和处理- 耗时的差异,可以体现任何页面的客户端通用优化效果
- 可以衡量SSR(服务端渲染) 页面的可消费耗时,和CSR(客户端渲染)页面的首帧耗时
webView.evaluateJavascript(
script = “(function(){return performance.timing.domInteractive;})();”,
callback = { value ->
responseEndDuration = value.toLong() - getOnCreateTimestamp()
}
)
虽然 WebKit 负责维护 Performance Timing 的值,但是 WebView 并未提供接口获取上述时间点的值。
三、优化方案和效果
1. 优化方案概述
基于客户端内 Web 页面的加载流程,从 “WebView 初始化耗时优化”、“资源加载耗时优化”、“逻辑处理耗时优化” 三个方面,提出了 5 个优化项。
- TBS (X5 内核) 环境预加载
- WebView 实例池
- 主请求并行加载
- Web 公共资源池
- 跟肤逻辑优化
各优化项在 Web 页面加载过程中的生效时机如下:
2. 优化手段说明
(1)WebView 初始化
经过前期分析,WebView 初始化耗时本身的耗时压缩空间比较有限。因此优化手段主要以初始化逻辑前置为主。例如,“WebView 实例池” 通过在应用位于后台、主线程卡顿影响不明显的时机进行 WebView 预初始化,置换启动 Web 页面时的初始化耗时。
(2)客户端自建缓存
为了实现前述各项资源加载优化,客户端需要独立于 WebView 的缓存机制,自建一个资源缓存。
自建缓存参考客户端常用的三级缓存机制,基于 WebView 的强生命周期,设计了 “冷-热缓存循环” 的缓存生命周期。
例如,在 WebView 初始化的同时,自建缓存把页面需要的资源从文件系统加载到内存;向 WebView 资源拦截回调输入字节流时,自建缓存一定从内存缓存中输出,输出完毕后即可立即从内存缓存中被清除。这一机制可以使内存缓存的淘汰更积极,字节流在内存中停留的时间更短,减少内存占用。
(3)公共资源内联
在完成公共资源池开发后,页面打开耗时出现了负优化的情况。经过分析,确定与资源拦截回调的性能瓶颈有关。
- 单线程模型导致读写性能下降
- 被拦截资源的数量越多,对性能的影响越容易被放大
因此,为了减少资源拦截回调的性能影响,从减少拦截次数的角度,引入了公共资源内联优化。
- 公共资源加载到热缓存后,转换为对应的 HTML 节点
- 主请求并行加载完成后,直接在主请求字节流中替换其对应的外联节点;替换后的新字节流返回 WebView
引入公共资源内联后,基本抵消了资源拦截回调的性能影响,页面加载耗时提升 3.2%。
3. 优化效果
QQ 音乐 Android 端内评论页:
- 加载耗时降低 26.2% (1932ms → 1426ms)
- 跳出率降低
- 停留时长中位数增加
四、跨端场景的瓶颈与对策
基于在 WebView 场景下的优化过程,推及跨端场景可能存在的类似问题,本文尝试给出一些跨端场景中可能的性能瓶颈及应对方式。
1. 前终端通信通道效能不足,考虑 “少次多量”
跨平台方案 (WebView、React Native 等) 普遍存在前终端通信通道效能不足的问题。
- WebView 通道不支持较大量级数据的传递
- 通信线程多为单线程,甚至需要在主线程发起或处理通信
- 对传递次数的敏感程度大于对传递数据总量的敏感程度
因此,当在跨端场景出现大数据量传递时,需要优先考虑当前通信通道的可用性。在需要传递数据总量无法压缩的情况下,如果通道允许,尽量减少传递次数,增加单次传递的数据量。
“公共资源内联” 即是这一思路的实践。
2. 扩展生命周期
前端生命周期有限。客户端可以利用在前端生命周期以外的时间,进行适当的资源前置和逻辑前置,降低页面加载耗时。
例如上述优化中的 “公共资源池”、“主请求并行加载” 等,体现了扩展生命周期的思想。除此之外,微信小程序的双线程模型[1]通过引入 JSCore,增加前端代码的可执行时长,并通过离线包等手段帮助前端扩展生命周期。
3. 精简 / 前置公共库代码
如果前端页面共用公共库,随着前端业务的复杂化,公共库的自然膨胀,可能会放大脚本解析与执行的耗时。
针对 Web 页面,可以通过精简基础库的方式,减少无关代码的执行;针对 React Native 页面,可以通过进行分包和实例预加载,让更多基础库代码在页面加载前执行,从而降低页面启动时执行的代码量,减少耗时。
五、总结与展望
本文基于客户端内 Web 页面的加载特点,针对 WebView 初始化、资源加载和逻辑处理现状中的问题和瓶颈,设计并实施了 5 个优化项,优化效果比较明显。并且尝试对跨端场景的瓶颈与对策进行归纳,尝试为后续跨端场景的优化工作提供思路。
写在最后
在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。
如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!
加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!