3. 埋点需求分析:确定要采集什么

启动阶段的事情特别多,但埋点不能什么都打。我见过最夸张的项目,一个启动流程埋了三百多个点,最后分析时发现一半都是脏数据。所以,在动手写代码之前,先搞清楚一个问题:到底需要采集哪些数据?

说白了,这就是埋点的需求分析。你要把启动过程里的用户行为、系统状态、业务指标都拆开来看,然后给它们排个优先级。否则你采集了一大堆,最后发现关键的几个漏掉了。

一个小原则:宁可少埋十个无关的点,也不要漏掉一个关键的点。我后续会讲怎么判断什么算“关键”。

3.1 启动阶段的数据分类

我个人习惯把启动阶段的数据分成四大类,你可以拿这个框架对照自己的项目。

类别 典型数据点 采集目的
系统环境数据 设备型号、Android版本、内存大小、存储空间、网络类型 判断启动慢是不是硬件或系统环境的问题
应用状态数据 安装来源、上次使用版本、是否冷启动、是否有缓存 区分首次启动还是常规启动
用户操作数据 点击启动图标、点击通知、点击快捷方式、被系统拉起 分析用户的启动入口偏好
性能指标数据 Application.onCreate耗时、onCreate时间、首帧渲染时间、启动完成时间 量化启动速度,做性能优化

嗯,这里要特别注意系统环境数据。我在项目中遇到过一位同事,他把 CPU 核心数都埋进去了,实际上大多数设备都是 8 核,这个数据基本没用。建议只采集那些在性能问题定位中真正能用得上的,比如低内存状态、低存储状态这些。

3.2 用户行为 vs 系统状态

很多同学会把这两个概念搞混。举个例子:用户点击了启动图标,这叫用户行为;系统在 Application.onCreate 里做了初始化,这叫系统状态。两者要分开考虑,但又不能完全割裂。

你想想看,用户行为是“谁在什么情况下做了什么”,系统状态是“当前系统和应用处于什么水平”。我们埋点的目的,就是要把这两个维度对应起来。比如:

  • 用户行为 + 系统状态 = 完整的启动场景
  • 用户行为 + 性能指标 = 用户对启动速度的真实体验
  • 系统状态 + 性能指标 = 技术优化方向
实战建议:我一般会在埋点事件里同时携带两个维度的数据。比如“冷启动”这个事件,除了记录启动方式是冷启动,还会带上当时的内存占用值和上一次退出的时间戳,这样后续分析起来才能串起来。

再补充一个容易踩坑的点:系统状态数据不要只采集一次,启动过程中不同阶段的状态可能不同。比如刚启动时内存可能很充足,但初始化了几个库之后就紧张了。我曾经因为只采集了启动开始时的内存,结果漏掉了某个第三方库加载时的内存峰值。

3.3 优先级排序:哪些数据必须采集

启动流程涉及十几个方法回调、几十个初始化步骤,不可能每个都埋点。我通常用“影响面 × 问题频率”这个矩阵来排序。

优先级 条件 示例
P0(必须) 影响整个启动时长,且经常出问题 Application.onCreate总耗时、主线程耗时操作、崩溃点
P1(重要) 影响面大但频率低,或频率高但影响面小 某个核心库的初始化耗时、网络首次请求耗时
P2(可选) 影响面小且频率低 个别第三方SDK的版本号、A/B实验变量

排序时,我个人习惯先列出所有可能的埋点候选,然后问自己三个问题:

  1. 这个数据丢了,我能不能排查出启动慢的问题?—— 不能?那就是P0。
  2. 这个数据变化了,会不会直接影响用户感知?—— 会?那就是P1或以上。
  3. 如果只能保留10个埋点,我会保留哪10个?—— 这样一逼自己,很多无关的数据就自动过滤掉了。
注意:不要为了“以后可能有用”而随便加埋点。埋点越多,数据处理和存储成本越高,而且容易污染核心数据。我曾经因为想着“多埋点总没错”,导致启动阶段多开了三个线程去写日志,反而拖慢了启动速度——教训深刻。

3.4 如何确定启动阶段的行为边界

启动什么时候算开始?什么时候算结束?不同定义会导致采集的数据完全不一样。我见过有的团队把“用户看到主界面”当作启动完成,有的把“网络数据加载完毕”算完成。我建议用“用户可交互”作为标准,因为这是用户感知到的启动完成。

对应的,需要采集的数据也要围绕这个边界来设计:

  • 开始点:Process.start() 或者 Application.attachBaseContext()?我个人习惯从 Application.onCreate() 开始,因为这是开发者可以控制的第一个点。
  • 结束点:Activity.onResume() 之后,用户能看到界面并且能点击。
  • 中间关键阶段:ContentProvider初始化、首帧绘制、数据预加载、网络请求等。每个阶段都需要有一个开始和结束的时间戳。

你可能会问:为什么强调时间戳?因为在分析时,你要知道时间消耗在哪个阶段。有了阶段时间轴,你才能精准定位瓶颈。后面章节我会具体讲怎么采集时间戳。

小技巧:如果你不确定某个阶段该不该埋,先大胆埋上,然后在正式上线前做一轮数据看板检查。如果发现某个埋点的数据全是同一数值,或者波动极小,就说明这个点可以去掉。我记得有一次我埋了“获得Wakelock”的次数,结果发现100%的冷启动都会持有一个短暂的Wakelock,这个数据就毫无价值。

3.5 梳理知识结构图

下面这张图,把启动阶段埋点需求分析的核心逻辑串起来了。你可以把它当作脑子里的一张地图,后面写代码时随时对照。

启动阶段埋点需求分析框架 启动触发 用户行为数据 系统状态数据 性能指标数据 点击图标 / 通知 被系统拉起 设备型号 / Android版本 内存 / 存储 / 网络状态 各阶段耗时 首帧渲染时间 优先级排序 影响面 × 问题频率 三类数据各自细分后,合并到优先级排序,选出P0/P1核心埋点

3.6 避坑清单

最后列几个我踩过的坑,你可以直接拿去做 checklist:

  • 别把操作系统调度行为当作应用行为。比如“冷启动时CPU频率变化”这个数据,它不是我们的 App 能控制的,埋了也没用。
  • 注意隐私合规。用户行为数据不能包含个人身份信息,比如 Android ID、IMEI 这些,除非你有明确授权。我在某次合规审查中因为这个被怼过。
  • 启动阶段埋点要轻量。不要在启动过程中做网络请求、写大文件,否则你埋的是启动性能本身。我一般用内存队列+异步写库的方式。
  • 优先级不是一成不变的。新版本如果修改了某个初始化逻辑,对应的埋点优先级可能需要调整。记得在每次发版前重新 review 一遍埋点列表。