QQ音乐 Android 客户端的 Web 页面日均 PV 达到千万量级,然而页面的打开耗时与 Native 页面相距甚远,需要系统性优化。本文将介绍 QQ 音乐 Android 客户端在进行 Web 页面通用性能优化过程中的问题、思路、方案和效果,并尝试对跨端场景的常见瓶颈和对策进行归纳。文章作者:关岳,QQ音乐客户端开发工程师。

一、问题与目标

作为一款注重于内容运营的应用程序,QQ 音乐 Android 客户端的 Web 页面日均 PV 达到千万量级,评论页、MV 页等核心页面均有 Web 页面参与,或完全由 Web 实现。

客户端内 Web 页面的打开耗时与 Native 页面相距甚远,需要系统性优化。然而,现有的前端和跨端优化方案,存在一定局限性。

1. 前端优化的局限

针对 Web 页面的耗时优化,在优化思路、方案、服务、工具链等方面都已经建设得非常详细。然而,在客户端内 Web 页面这一场景,纯前端优化存在以下两个局限:

  • 无法规避 WebView 初始化耗时

  • 受限于 WebView 生命周期范围

从客户端角度,除了思考优化 WebView 初始化耗时之外,还可以从 “扩展前端生命周期” 的角度出发,思考优化方案。

2. 跨端优化的局限

现有跨端优化方案,包括离线包、VasSonic 等,为了达到最好的优化效果,均需要前端终端共同参与改造。这导致存量页面的逻辑改造增加,对线上页面不够友好,引入额外的成本和风险。在前端开发资源不足时,这些优化的开展存在一定难度。

从减少前端开发工作量的角度来看,需要思考更具通用性、前端感知更小的优化方案。

3. 目标

基于本次优化的背景,本次优化提出以下两方面的目标:增强通用性、减少前端改造成本。

二、指标设计

在展开优化思路和实施的同时,需要建立衡量优化效果的性能指标。

1. 客户端现有性能指标数据

接下来基于客户端内 Web 页面加载过程,描述客户端现有性能指标代表的时机。

(1)客户端 WebView 回调

基于 Android WebView 的过程监控回调和页面框架能力,可以实现的性能监控包括:

其中,onMainFrameFinished 取第一个非主请求 (HTML) 的资源被拦截的时机。对于绝大多数页面来说,此时已经完成主请求 (HTML) 的下载,并已经开始解析;可以粗略代表主请求流程结束。

(2)W3C Performance Timing

与客户端回调相比,W3C Performance Timing 提供了更细致的加载过程信息,但是不包含 WebView 开始初始化的时间点。下图中仅列出部分:

2. 各端单独采集的局限

(1)前端采集的局限

  • 无法独立获取 WebView 开始初始化的时间点。

  • 想获取最精确的加载完成时间点,主要依赖手动埋点。

(2)客户端采集的局限

SSR (服务端渲染) 和 CSR (客户端渲染),页面内容可消费的时间点不一致。

对 WebView 页面加载周期来说:

  • CSR 页面需在前端页面框架加载后再展示数据,内容请求完成并上屏,发生在页面加载完成之后

  • SSR 页面的首次内容上屏可携带首屏数据,因此在页面加载完成之前,页面内容已经可以被消费

客户端回调时机不够完整或过于“苛刻”,测不准“页面内容可消费”的时间点。

通过追溯客户端 onPageFinished 的回调时机,发现对应的 Blink 代码要求必须满足:页面解析完毕、 没有正在下载的资源等条件。

按照这个标准,一旦存在某个图片一直处在加载中,但页面框架的其他内容均已处理完毕,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 个优化项,优化效果比较明显。并且尝试对跨端场景的瓶颈与对策进行归纳,尝试为后续跨端场景的优化工作提供思路。

未来,团队还将进一步丰富客户端与前端的协同性能监控,并允许前端通过更精细化的方式启动客户端 Web 页面框架。远期,还将尝试探索 CGI 前置、引入 JSCore 等手段,进一步提升特定场景下的 Web 页面加载耗时。

参考资料:

[1] 微信小程序的双线程模型:

https://developers.weixin.qq.com/ebook?action=get_post_info&docid=0000286f908988db00866b85f5640a

看腾讯技术,学云计算知识,关注云加社区

QQ音乐Android客户端Web页面通用性能优化实践的更多相关文章

  1. 移动 H5(PC Web)前端性能优化指南

    原文地址https://zhuanlan.zhihu.com/p/25176904?utm_source=wechat_session&utm_medium=social&utm_me ...

  2. WEB前端的性能优化

    转自:http://www.2cto.com/kf/201604/498725.html 网站的划分一般为二:前端和后台.我们可以理解成后台是用来实现网站的功能的,比如:实现用户注册,用户能够为文章发 ...

  3. 现代WEB前端的性能优化

    现代WEB前端的性能优化 前言:这只是一份学习笔记. 什么是WEB前端 潜在的优化点: DNS是否可以通过缓存减少DNS查询时间? 网络请求的过程走最近的网络环境? 相同的静态资源是否可以缓存? 能否 ...

  4. RHEL/CentOS通用性能优化、安全配置参考

    RHEL/CentOS通用性能优化.安全配置参考 本文的配置参数是笔者在实际生产环境中反复实践总结的结果,完全适用绝大多数通用的高负载.安全性要求的网络服务器环境.故可以放心使用. 若有异议,欢迎联系 ...

  5. etcd 性能优化实践

    https://mp.weixin.qq.com/s/lD2b-DZyvRJ3qWqmlvHpxg 从零开始入门 K8s | etcd 性能优化实践 原创 陈星宇 阿里巴巴云原生 2019-12-16 ...

  6. Lazy<T>在Entity Framework中的性能优化实践

    Lazy<T>在Entity Framework中的性能优化实践(附源码) 2013-10-27 18:12 by JustRun, 328 阅读, 4 评论, 收藏, 编辑 在使用EF的 ...

  7. 第17 章 : 深入理解 etcd:etcd 性能优化实践

    深入理解 etcd:etcd 性能优化实践 本文将主要分享以下五方面的内容: etcd 前节课程回顾复习: 理解 etcd 性能: etcd 性能优化 -server 端: etcd 性能优化 -cl ...

  8. 直播推流端弱网优化策略 | 直播 SDK 性能优化实践

    弱网优化的场景 网络直播行业经过一年多的快速发展,衍生出了各种各样的玩法.最早的网络直播是主播坐在 PC 前,安装好专业的直播设备(如摄像头和麦克风),然后才能开始直播.后来随着手机性能的提升和直播技 ...

  9. 手游录屏直播技术详解 | 直播 SDK 性能优化实践

    在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...

随机推荐

  1. 《Java并发编程的艺术》第5章 Java中的锁 ——学习笔记

    参考https://www.cnblogs.com/lilinzhiyu/p/8125195.html 5.1 Lock接口 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同 ...

  2. laravel查询常用的方式含义.

    find($id) 传值并返回一个模型.如果不存在匹配的模型,则返回null.findOrFail($id) 传值并返回一个模型.如果不存在匹配的模型, 它会抛出异常.first() 返回在数据库中找 ...

  3. 微信小程序入门基础

    微信小程序入门基础  视频教程(https://edu.csdn.net/course/detail/8456?pre_view=1) 第一章.认识小程序  1.工具的下载与安装  2.小程序代码构成 ...

  4. Dubbo面试专题

    Dubbo面试专题 1. 什么是dubbo Dubbo是阿里巴巴SOA服务化治理方案的核心框架,是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 2.  ...

  5. Redis自带压测工具(redis-benchmark.exe)

    redis做压测: 可以用自带的redis-benchmark工具,使用简单 压测命令:redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 压测需要 ...

  6. ajax前后端交互原理(7)

    7.ajax函数封装 7.1.实例引入 需求: 每秒钟请求一次服务器 获取到数据 实现: 把ajax进行封装 <!DOCTYPE html> <html> <head&g ...

  7. 【草稿】自定义ASP.NET MVC Html辅助方法

    https://www.cnblogs.com/myshell/archive/2010/05/09/1731269.html 在ASP.NET MVC中,Html辅助方法给我们程序员带来很多方便,其 ...

  8. C# @string $string $@string

    @string  保证换行后也属于同一个字符串  (请特别注意\r\n这样也会直接输入,不在起到换行的效果) string execSql = @" SELECT T1.ProcInstID ...

  9. 不就是语法和长难句吗—笔记总结Day2

    6.区别定语从句和同位语从句 I have a dream that sounds funny. (定语从句) I have a dream that I will become a rich man ...

  10. 收藏python开发各种资源官方文档

    http://json.cn/ https://cn.bing.com/ https://processon.com/ https://docs.djangoproject.com/en/1.11/r ...