title: 性能评分与发版质量门禁 chapter: '26.5' section: '26.5' status: finalized applicable_versions: Android 10 (API 29) - Android 17 (API 37) last_verified: '2026-08-15' last_source_verified_at: '2026-08-15' last_verified_against: Current Android 17 migration/behavior-change docs, Android vitals and Macrobenchmark docs, and Google Play staged/full-rollout and Publisher API docs retrieved 2026-08-15 confidence: high sources:
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 1.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 29.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 31.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 32.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 33.md
- type: official path: https://developer.android.com/topic/performance/benchmarking/benchmarking-in-ci
- type: official path: https://developer.android.com/topic/performance/benchmarking/macrobenchmark-metrics
- type: official path: https://developer.android.com/topic/performance/vitals
- type: official path: https://support.google.com/googleplay/android-developer/answer/6346149
- type: official path: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.tracks
- type: official path: https://developer.android.com/about/versions/17/migration
- type: official path: https://developer.android.com/about/versions/17/behavior-changes-all
- type: official path: https://developer.android.com/about/versions/17/behavior-changes-17
- type: official path: https://developer.android.com/guide/app-compatibility
- type: official path: https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview
- type: official path: https://developer.android.com/reference/kotlin/androidx/benchmark/macro/TraceSectionMetric
- type: official path: https://developer.android.com/topic/performance/vitals/index.html
- type: official path: https://support.google.com/googleplay/android-developer/answer/16285429
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 6.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 9.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 10.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 24.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 28.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 34.md
- type: legacy-reference-preserved path: Clippings/线上疑难问题该如何排查和跟踪?-Android开发高手课-极客时间 35.md
- type: official path: https://developer.android.com/topic/performance/app-score
- type: official path: https://developer.android.com/topic/performance/baselineprofiles/overview
- type: official path: https://developer.android.com/topic/performance/baselineprofiles/measure-baselineprofile
- type: official path: https://developer.android.com/android-performance-analyzer
- type: official path: https://developer.android.com/reference/android/os/health/SystemHealthManager
- type: aosp path: https://android.googlesource.com/platform/frameworks/base/+/android-17.0.0_r1/core/java/android/os/health/SystemHealthManager.java
- type: official path: https://developer.android.com/reference/android/os/PerformanceHintManager
- type: aosp path: https://android.googlesource.com/platform/frameworks/base/+/android-17.0.0_r1/core/java/android/os/PerformanceHintManager.java tags:
- quality-gate
- release
- canary
- rollback
- app-performance-score
- android-vitals
- macrobenchmark
- baseline-profile
- performance-governance
- observability related_chapters:
- '26.4'
- '26.1'
- '16.1'
- '16.3'
- '16.5'
- '22.10'
- '26.8' pipeline_stage: ready-to-publish task6_state: reviewed task9_state: reviewed task2b_state: fixed last_draft_polish_at: '2026-08-15T19:49:16+08:00' last_draft_polish_run_id: 20260815-194916-gracker-writing-468 last_review_finalize_at: '2026-08-15T19:49:16+08:00' last_review_finalize_run_id: 20260815-194916-gracker-writing-468 last_rework_at: '2026-08-15T19:49:16+08:00' last_rework_run_id: 20260815-194916-gracker-writing-468 last_consolidated_at: '2026-08-24' consolidated_from:
- src/part1-fundamentals/ch05-cpu-power/5.33-android17-performance-score-attribution-sourcecode.md
- src/part5-app/ch26-observability/07-release-quality-gate.md
- src/part5-app/ch26-observability/15-app-performance-score.md
性能评分与发版质量门禁
发版门禁用少量稳定指标阻止明确回归,Performance Score 用加权模型汇总多个性能维度。评分前必须固定场景、人群、设备层级和缺失数据处理,否则总分会掩盖局部恶化。
基线、阈值、置信度与阻断规则
门禁如何衔接发布阶段
发版质量门禁用一组可验证条件决定版本能否进入下一发布阶段。26.1 节负责采集性能指标,26.4 节负责实验设计和回归检测,本节把这些证据对应到发版前检查、候选包测试、灰度扩量、暂停和恢复动作。
本文覆盖 Android 10 到 Android 17(API 29–37),Android 17 源码标签为 android-17.0.0_r1。这里讨论的是应用和交付平台设计,不涉及某个 Linux 内核版本特有的实现,所以无需记录内核标签(kernel tag)。AOSP 标签用于核对平台实现;实际发布还要覆盖目标设备厂商(OEM)、系统构建指纹(build fingerprint,用于唯一标识设备上的系统构建)和季度更新。源码标签只能说明所核对的代码版本,不能代替真实设备验证。
发版前性能检查清单
发版前检查清单要能直接执行。每项至少说明被测产物、场景与设备、指标定义、比较基线、判定方法、失败动作和负责人。缺少任一项,数字变化便无法转换成明确的发布决定。
发布单应先冻结产物身份:versionCode(Android 用来判断版本新旧的整数)、Git 提交、AAB/APK 文件摘要、签名证书摘要、compileSdk、targetSdk、构建变体、R8(Android 的代码压缩、优化与混淆工具)映射文件、原生代码符号文件、Baseline Profile(预先提供给 ART 的热点代码配置)版本、动态特性模块版本和远程配置快照。测试报告与生产事件都引用同一个构建 ID,才能确认实验室和生产环境观察的是同一份代码与配置。
| 检查域 | 证据与口径 | 不通过动作 | 关联章节 |
|---|---|---|---|
| 启动 | 按启动类型、入口和设备群比较首次显示时间(TTID);仅在正确调用 reportFullyDrawn() 的场景使用完全显示时间(TTFD) | 阻断候选包,结合 Perfetto Trace(系统跟踪文件)检查启动路径 | 21.1、26.1 |
| 渲染 | 核心交互的 FrameTimingMetric 分布、生产环境慢帧或冻帧指标,并按刷新率和页面分组(分群) | 阻断或缩小发布范围 | 22.10、26.1 |
| 内存 | Java 堆、原生堆、PSS(按比例分摊共享页后的物理内存)、RSS(包含共享页的驻留物理内存)、内存不足(OOM)与低内存退出;同时记录进程状态和设备内存档位 | 阻断高风险设备群,补充堆转储、Perfetto 或退出记录 | 23.7、26.2、26.6 |
| 稳定性 | 崩溃、应用无响应(ANR)、原生崩溃、启动失败和 ApplicationExitInfo 退出原因,按受影响用户数与事件数分别呈现 | 停止扩量,进入回滚评估 | 20.1–20.4、26.2、26.6 |
| 功耗 | 固定场景的实验室能耗与生产环境异常唤醒、后台任务证据;声明测量是否覆盖整台设备 | 对耗电路径限流或关闭配置 | 25.1–25.3、26.11 |
| 数据健康度 | 实验分组(assignment)、采样配置、事件生成、落盘、上传和查询延迟 | 将业务指标标为不可判定,暂停扩量 | 26.1、26.4 |
| 包体与配置 | AAB/APK 与动态特性大小、资源变化、远程参数和实验快照 | 回退配置或重新生成候选包 | 25.5–25.11、26.4 |
每个检查域都要在发布单中留下证据链接和判定结果;任一项失败时,直接执行表中动作并记录审批人。
Android 17 要做两轮兼容性验证
Android 官方迁移指南把升级验证分成两条可以并行推进的路径:
- 运行兼容性:把当前生产版本安装到 Android 17 设备,保持原有
targetSdk,验证所有应用都会受到的行为变化。这里发现的问题也会影响已经发布的包,通常应优先处理。 - 目标版本兼容性:用 API 37 SDK 构建并把
targetSdk升到 37,验证只对目标 API 37 及以上生效的变化。Android 17 的兼容性开关允许在可调试包上单独启用某项变化,便于隔离原因;发布结论仍要来自按目标 SDK 构建、配置接近生产版本的候选包。
测试清单应从官方“影响所有应用”和“以 Android 17 为目标版本”两份行为变化文档生成,并在每轮验证时更新。对目标 API 37 及以上的应用,新的无锁 MessageQueue 实现可能破坏依赖其私有字段或方法的反射代码;通过反射修改 static final 字段会抛出 IllegalAccessException,通过 JNI 修改会导致崩溃。证书透明度(Certificate Transparency,公开记录并校验网站证书签发情况的机制)默认启用;通过 System.load() 动态加载的原生库文件必须为只读。后台音频限制和大屏方向、宽高比及可调整大小规则也可能影响媒体或布局。应用可以只选择与自身代码路径相关的条目,但每个排除项都要留下理由。
兼容性开关适合逐项定位问题;用户设备运行的是多项行为变化同时生效的系统。正式候选包还要在 Android 17 的完整行为组合下运行主流程、后台流程、升级安装、数据迁移、权限拒绝、进程重建和大屏场景,并检查受限非 SDK 接口告警及第三方 SDK 的兼容性。
阈值必须来自本项目数据
门禁阈值应绑定指标版本、设备群、场景和发布阶段。相对退化阈值来自历史噪声与业务可接受的最小变化,绝对阈值来自体验服务等级目标(SLO)或稳定性预算,最小样本量取决于历史方差和希望识别出的变化幅度。其他项目的百分比、毫秒数或样本量没有这些前提,不能直接复制进配置。
Android vitals 是 Google Play 汇总的外部质量信号。Play 每天用最近 28 天的平均值检查关键指标。当前面向所有应用的 Core vitals(会影响应用在 Google Play 中曝光度的核心指标)包括用户感知崩溃率、用户感知 ANR 率和过度持有局部唤醒锁;过度耗电只作为表盘应用的 Core vital。发布报告要分别保存自建应用性能监控(APM)与 vitals 的分子、分母、统计窗口和设备范围。内部会话数与 Play 的用户或会话口径不同,不能放在同一个分母中计算。
自动化性能测试集成
自动化测试无法覆盖全部真实设备,它的职责是让候选包带着可复现证据进入灰度。风险较高的候选包如果没有实验室性能报告,应停在发布候选阶段。
Android 官方建议在物理设备上运行 Benchmark;模拟器结果会受宿主机和虚拟化环境影响,不宜代表用户体验。目标 APK 应接近生产构建:不可调试、允许性能剖析(profiling)、使用一致的 R8、资源压缩、签名后处理和 Baseline Profile。测试 APK 与目标 APK 分开构建,避免把 Benchmark 专用配置带进生产包。
Macrobenchmark 指标进入门禁前,要核对 API 可用范围和测量语义:
StartupTimingMetric.timeToInitialDisplayMs测量首帧出现所需时间。timeToFullDisplayMs依赖reportFullyDrawn(),在 Android 10 / API 29 及更早版本可能不可用;没有明确完整绘制点的场景不能用 TTFD 卡门禁。FrameTimingMetric.frameDurationCpuMs是界面线程(UI Thread)与渲染线程(RenderThread)生成一帧所用的 CPU 时间;frameOverrunMs只在 Android 12 / API 31 及以上可用,表示相对于帧截止时间的提前或超时量。两项指标不能共享阈值。TraceSectionMetric仍标记为实验性 API,用来统计 Trace 中带名称的代码区段(section)。AndroidX Benchmark 1.3.0 及以上默认只统计目标包(targetPackageOnly = true),并以Mode.Sum汇总一次测量中所有同名 section 的耗时;若要看第一次或最长一次,必须显式选择Mode.First或Mode.Max。业务阶段可能重复出现时,应先确定所需的聚合方式。PowerMetric仍标记为实验性 API,测量的是系统级功率或能量,无法直接归因到单个应用;官方支持 Pixel 6、Pixel 6 Pro 及后续设备,测试时还需减少其他进程的干扰。
门禁配置无需嵌入一组脱离项目数据的示例数字。可移植的是这些字段及其约束:
| 字段 | 要回答的问题 |
|---|---|
artifact_id / commit | 测的是哪一份目标包与测试包 |
scenario_id / setup_version | 用户路径、账号和测试数据是否一致 |
metric_name / metric_version | 指标怎样计算,当前 API 是否可用 |
device_id / build_fingerprint | 设备、系统镜像和刷新率是否可比较 |
compilation_mode / profile_version | 编译状态和 Baseline Profile 是否一致 |
baseline_id | 比较对象是已知质量合格的固定版本、近期滚动趋势,还是人工固定基线 |
sample_count / effect_interval | 测量次数、效应值和不确定区间分别是什么 |
decision / owner / waiver_expiry | 失败后做什么,由谁处理,豁免何时失效 |
baseline_id 不宜永久指向前一轮构建。该构建可能已经退化,连续的小变化也可能被滚动比较忽略。已知质量合格的固定版本适合判断累计漂移,近期稳定趋势适合识别设备环境变化,人工固定基线适合重大重构;报告必须显示实际使用的基线及其更新时间。
Benchmark 库输出测量 JSON 和性能剖析 Trace;Macrobenchmark 会为每次测量迭代生成一份 Perfetto Trace。持续集成(CI)系统应按构建、设备、场景和测试名归档 JSON 与 Trace。失败报告除通过或不通过状态外,还应包含新包值、基线值、效应区间、测试轮数、热降频(设备过热后主动降速)等环境标记、失败迭代的 Trace 和候选提交。
拉取请求(PR)阶段适合运行编译校验和试运行(dry run),确认测试可以执行;固定物理设备上的重复测量更适合主干定时任务和候选包门禁。单次噪声较大的 Benchmark 不足以自动判定业务回归。门禁要区分测试基础设施失败、环境漂移和可重复的应用退化,并为每类失败定义不同动作。
灰度发布与性能监控联动
灰度发布(staged rollout)先把候选包提供给一部分符合条件的用户,再分阶段提高比例。实验室测试决定能否开始灰度,生产数据决定能否扩大覆盖,数据健康度决定当前窗口能否支持发布判断。
Google Play staged rollout 只适用于应用更新,首次发布不能使用。Play 会为每个新版本随机选择符合条件的用户;暂停后恢复仍影响同一组用户。暂停会阻止更多用户取得该版本,已经安装的用户仍停留在该版本。
staged rollout 不满足严格 A/B Test 的设计条件。Play 不向开发者提供由实验协议定义、可以稳定复现的旧版本对照组;版本覆盖还会受国家、设备资格、自动更新、安装时间和渠道影响。因此,版本间差异可以触发风险处置,但仅凭“灰度用户比旧版用户差”无法证明代码变化就是原因。因果判断仍需使用 26.4 节的受控实验或可复现回退证据。
灰度监控要按版本对象(release)和每次扩量批次记录,不能只按 versionName 聚合。门禁系统至少记录:
track(生产、开放测试等发布轨道)、版本名称(release name)、versionCode与发布事务的 edit ID。rollout_id、目标userFraction、国家范围,以及开始、暂停、恢复和完成时间。- build ID、产物摘要、签名和符号文件版本。
- Remote Config(远程配置)、实验、服务端开关和后端依赖版本快照。
- 指标窗口的事件时间、入库时间、数据已完整到达的最晚事件时间,以及查询时间。
- 发布前登记的设备、Android 版本、国家、渠道、刷新率、入口和新老用户类别。
userFraction 表示有资格接收该 staged release 的用户比例。它既不表示安装完成率,也不表示实时在线用户占比。发布平台要同时观察符合资格、已更新、已启动、产生指标和上传成功的数量;只用目标比例估算样本量会高估有效暴露。
发布门禁可以采用以下状态机;具体比例和观察时间由流量周期、事件发生率、审核时延与风险预算决定,不写成全项目通用常量。
| 状态 | 进入条件 | 继续条件 | 异常动作 |
|---|---|---|---|
| 内部验证 | 候选包与配置冻结,自动化门禁通过 | 安装、升级、主流程和诊断上报可用 | 重新构建,不进入生产 |
| 初始生产灰度 | 内部证据齐全,发布审批完成 | 快速稳定性指标与数据健康度可以判断,未发现高风险分群 | 暂停版本,保存诊断证据 |
| 扩量观察 | 前一阶段通过,样本覆盖预先登记的分群 | 指标区间在预算内,服务端与客户端依赖稳定 | 保持当前比例或限制国家、设备 |
| 完成发布 | 风险负责人接受剩余不确定性 | 全量后继续观察版本队列、vitals 与反馈 | 暂停已全量版本、降低配置风险或发布修复包 |
灰度决策要区分快速信号和慢速信号。自建 APM、崩溃与 ANR 事件流、启动与帧指标可以较早暴露风险,前提是数据延迟和样本覆盖达标。Android vitals 每天更新最近 28 天的平均值,适合观察长期质量与 Play 警告,无法为刚开始的小流量灰度提供即时扩量依据。
客服反馈和商店评论到达较慢,也存在选择偏差。它们可以帮助发现未知症状;“暂时没有投诉”不能抵消已经观测到的崩溃、ANR 或数据管道异常。
APM 与发布平台需要双向核对。发布平台向监控侧提供版本、目标比例、国家、配置和实验快照;监控侧把指标区间、异常分群、数据完整性和证据链接写回发布单。若上报成功率、事件丢弃、配置命中率或上报延迟异常,当前结论应标为“不可判定”,维持或暂停当前比例。缺失数据不能解释为质量正常。
版本回滚决策流程
Android 应用所说的“回滚”至少包含三种操作:暂停发布、回退服务端配置,以及发布更高 versionCode 的修复包。配置错误可以通过关闭开关处理;未完成的 staged rollout 可以暂停;已经安装到设备上的问题版本无法由 Play 自动降级,需要借助配置降级、服务端兼容或修复包恢复功能。
Google Play Developer API 的版本状态包括 draft(草稿)、inProgress(分阶段发布中)、halted(已暂停)和 completed(发布完成)。userFraction 只允许用于 inProgress 或 halted,取值必须严格大于 0 且小于 1。暂停进行中的版本时,把状态更新为 halted 并提交 edit(一次待提交的发布事务);该版本随后不再提供给更多用户,已安装用户不受影响。
Google Play 也允许暂停已全量发布的版本,但内部测试轨道除外。当前版本不能是该轨道的首个版本,并且前一个已全量发布版本不能存在阻止重新提供的政策违规。暂停成功后,前一个版本会重新提供给新用户和其他符合条件、尚未安装问题版本的用户;已经安装问题版本的用户仍不会自动降级。发布平台执行前应读取轨道当前状态,显示将恢复提供的版本号;执行后再次读取状态确认结果。
按可逆性和剩余影响面选择动作:
| 信号 | 判断 | 推荐动作 |
|---|---|---|
| 配置导致启动请求增加、日志量激增、图片预加载范围扩大 | 可通过服务端配置恢复 | 立即回退配置,保留版本灰度 |
| 初始灰度出现崩溃、ANR 或启动失败异常 | 影响范围仍受控,包体风险高 | 暂停 staged rollout,冻结证据并复现 |
| 特定设备群出现稳定退化 | 可通过发布资格或功能开关缩小范围 | 保持当前比例,限制受影响范围并准备修复 |
| 已全量版本出现严重稳定性问题 | 仍有用户可能更新到问题版本 | 核对可恢复提供的旧版本后暂停,同时降低配置风险并提交修复包 |
| 已安装用户持续受影响 | 暂停发布无法降级现有安装 | 保持服务端兼容、关闭高风险功能、提供应用内提示,并发布更高 versionCode |
| 数据完整性失败 | 当前质量结论不可用 | 暂停扩量,修复监控数据通路;已有明确安全信号仍按该信号处理 |
自动化可以生成建议、检查权限和准备 Play edit,但暂停、恢复和完成发布等动作会改变用户能够取得的版本,应保留明确审批、操作者、请求内容和 Play 返回结果。重试前先读取当前轨道,避免网络超时后重复修改未知状态。
回滚证据包至少包含:轨道、版本、versionCode、构建 ID、目标覆盖率与有效覆盖率、异常指标定义、基线与效应区间、数据完整性、受影响分群、按堆栈指纹归组的崩溃或 ANR 问题、Trace 或日志样本、配置快照、可恢复提供的旧版本、已执行动作和负责人。对于动态配置,还要保存旧值、新值、作用条件、配置版本和客户端生效时机。
处置后要验证恢复是否与动作时间一致。配置回退要检查配置拉取、激活与功能实际使用的转化步骤;修复包要检查新 versionCode 的有效覆盖和关键指标;Play 侧长期质量继续观察 vitals 的滚动窗口。恢复可以证明处置有效,根因仍要由代码、Trace 或受控实验确认。事故中发现的设备群、场景或数据缺口应加入下一版门禁。
发布单要保存完整证据
发布单既是审批记录,也是事故复盘和下一轮门禁的输入。每个发布单至少保存四类附件:CI Benchmark 报告、灰度监控快照、配置或实验快照、人工审批与豁免记录。豁免要写明规则、原因、证据、责任人、适用版本和失效时间;新版本不得自动继承。
发布单还应保存每次状态转换,不能只保留当前状态:谁在什么时间依据哪一版数据把版本从候选包转为灰度、扩大到哪个比例,以及何时暂停、恢复或完成。不可变的事件记录可以帮助团队复原退化出现的时间窗口、当时生效的开关和继续发布的依据。
Performance Score:分项、证据与工程归因
单指标门禁明确后,综合评分用于排序和趋势观察。总分变化必须能够展开到原始指标、设备和版本。
团队拿到一个 0~100 的性能分数后,需要判断它能否转化为可复测的工程任务。App Performance Score 是 Google 在 2026 年仍标为 Preview 的评估框架,包含静态与动态两类评分:静态评分检查源码配置和工具采用情况,动态评分测量指定物理设备上的运行表现。
分数只表示评估表中还有多少改进空间,无法概括线上用户体验。团队仍需维护自己的 KPI(关键绩效指标,用于持续衡量业务或质量目标)。评分项可转换成配置修正、自动化路径、trace 分析和发布验证四类任务;trace 是按时间记录线程、调度、I/O 等事件的性能跟踪文件,Android 17 平台指标可作为环境旁证。
26.1 介绍端侧性能采集,26.4 介绍实验统计,26.8 说明 Android Vitals 与 Play 的线上口径。这里讨论评分到行动的映射,不重复这些章节的采集实现。
App Performance Score 的定位
App Performance Score 适合研发阶段的快速评估。官方页面给出 0~100 分,低分表示改进空间较大;静态分和动态分可以分别使用。Play Console 使用线上数据判断发布质量和商店可见性,App Performance Score 则用于研发评估,两者用途不同。评分规则、评估方式和建议仍可能随着 Preview 迭代。
它和常见工具的边界可以这样划分:
| 工具或系统 | 回答的问题 | 适合阶段 | 不适合做的事 |
|---|---|---|---|
| App Performance Score | 工程配置和受测路径是否存在评分表覆盖的缺口 | 研发评估、专项立项、版本验收前 | 不能直接给出根因,也不能替代线上监控 |
| Android Vitals / Play Console | Play 用户最近窗口内是否出现坏行为,是否影响商店可见性 | 线上质量裁决、版本趋势复核 | 数据有窗口延迟,不能替代实时报警;详见 26.8 节 |
| Macrobenchmark | 某条启动、滚动或页面路径在受控设备上的耗时与 trace 证据 | CI、专项回归、性能预算 | 不能覆盖所有真实用户路径;脚本质量决定结论质量 |
| Perfetto / Android Studio Profiler | 某次慢启动、慢帧或线程调度异常的时间线证据 | 根因定位、案例复盘、前后 trace 对比 | 单次 trace 不能代表用户总体分布 |
| 自建应用性能监控(APM) | 版本、设备、渠道、用户路径上的长期指标和报警 | 灰度、发布、线上治理 | 指标口径容易漂移,需要记录定义和版本 |
评分项命中后要回到受测场景。启动问题结合 TTID(首次画面显示所需时间)、TTFD(完整内容可用所需时间)、主线程和进程状态;渲染问题结合 FrameTimeline(记录每帧预期与实际时间线的平台数据)、UI thread、RenderThread、SurfaceFlinger 与 GPU;设备差异结合 SoC(系统级芯片,即设备的主处理器平台)、内存、存储、温度和后台负载。分数用于选择调查方向,根因需要可复现路径与 trace。
静态评分:低成本配置项先补齐
静态评分不运行 App,需要读取项目源码。官方当前列出的项目包括:使用最新 Android Gradle Plugin、以 full mode(完整优化模式)启用 R8 并把 keep 例外限制在必要范围、正确应用 Baseline Profiles 且覆盖至少一条用户路径、用 Startup Profiles 优化 DEX 布局、采用最新稳定版 Compose,以及在内容可用时调用 FullyDrawnReporter 或 reportFullyDrawn()。
keep 规则会阻止指定代码被裁剪、优化或改名。Baseline Profiles 是随应用发布、供 Android 运行时(ART)预编译关键代码路径的规则集;Startup Profiles 则指导 R8/D8 调整启动代码在 DEX 文件中的排列。
这些项的价值在于成本低、失败信号清楚、适合接进持续集成(CI)。
| 静态项 | 检查方式 | 改进收益 | 推荐归属 |
|---|---|---|---|
| Android Gradle Plugin 版本 | CI 读取插件解析后的 AGP 版本,不只搜索根工程文本 | 获取当前 R8 与 profile 工具链能力 | 构建负责人 |
| R8 full mode 与例外控制 | 检查 release variant 的 minify 配置、优化模式与 keep 规则范围 | 降低代码体积并启用优化 | 架构 / 构建负责人 |
| Baseline Profile | 验证生成任务、产物内 profile 与关键路径覆盖 | 让所含代码路径从首次运行起获得 AOT(安装时预编译)优化 | 性能专项负责人 |
| Startup Profile | 检查 startup-prof.txt 是否生成并被 release 构建消费 | 改善启动期 DEX 布局 | 启动专项负责人 |
| Compose 稳定版本 | 解析 version catalog、BOM 与直接依赖后的版本 | 获取当前稳定版本的性能与修复 | UI 基建负责人 |
reportFullyDrawn() | 检查各启动入口是否在内容可交互时报告 | 为 TTFD 提供业务完成点 | 业务页面负责人 |
Baseline Profiles 官方说明给出的总体经验是,纳入 profile 的路径从首次运行起可避免解释执行和部分 JIT(即时编译)成本,许多应用测得约 30% 的代码执行性能改善。这个数字来自多款应用的测量经验,具体收益取决于路径覆盖、设备、构建配置和测量方式。
Startup Profiles 在构建时影响 DEX 布局,官方建议与 Baseline Profiles 同时使用。
profile 生成构建与发布构建的配置不同:生成 profile 的 variant(构建变体)应关闭 R8 混淆和优化,以保持规则与方法签名可匹配;最终 release 则应启用 R8,构建工具会把规则重写到优化后的代码。CI 若只检查仓库中存在 baseline-prof.txt,无法证明 release 产物已经包含且使用 profile。
静态检查应成为版本基线。新用户路径没有 profile 覆盖、keep 规则范围扩大、release 关闭 R8、关键启动入口缺少 TTFD 报告,都应生成带模块、产物和负责人信息的诊断结果。
动态评分:用真实设备校验用户路径
动态评分依赖运行时数据。官方要求使用物理设备,并建议覆盖能代表用户群体的多台设备;低端设备可放大性能差异。分数属于该设备、该构建和该次观察条件,不能脱离这些条件横向排名应用。
当前动态评分覆盖两类指标。slow frame 与 frozen frame 是官方对超出渲染时限和长时间停顿帧的分类;本文保留英文名称,以免和团队自行定义的卡顿阈值混为一谈。
| 动态类别 | 官方评估口径 | 工程侧补充字段 | 关联章节 |
|---|---|---|---|
| Application startup | 从启动到 App 可交互的持续时间,口径指向 TTFD | 启动模式、入口来源、首屏 Activity、进程与编译状态、设备档位 | 21.1、26.1 |
| Rendering performance | 滚动、动画和全屏渲染中的 slow frames / frozen frames 占比 | 页面、刷新率、列表数据量、图片数量、是否 Compose、是否 SurfaceView / TextureView | 22.10、26.1 |
动态评分要分三档投入。
| 档位 | 做法 | 适用场景 | 主要风险 |
|---|---|---|---|
| 手动评估 | 固定构建、设备和前置状态,人工执行路径,记录分数与现象 | 新项目初筛、专项启动前 | 操作差异大,难以稳定复测 |
| Macrobenchmark | 建独立 com.android.test benchmark module,用脚本驱动启动、滚动、动画路径,输出 JSON 和 trace | CI 回归、版本对比、性能预算 | 脚本覆盖的只是选定路径 |
| 设备池自动化 | 在低端、主流、高刷、低存储和不同温度状态下运行同一组本地可重复路径 | 发版门禁、灰度前验收 | 设备维护成本高,环境漂移会污染结果 |
Macrobenchmark 官方文档要求测试位于独立 com.android.test 模块。Macrobenchmark 是从应用外部驱动完整用户路径的宏基准测试。
被测 App 需要开启 profileable,让 shell 工具能在非调试构建上读取详细 trace 信息;构建应接近生产,关闭 debuggable 标志,并启用 minification(代码压缩与优化)。库会输出控制台结果、JSON 和 trace;它用于建立可重复指标,覆盖范围仍由测试脚本决定。
TTFD 依赖 App 在合适时机调用 reportFullyDrawn()。如果首页首帧已经显示,但核心内容尚未加载或输入仍被阻塞,报告点就不应停在 Activity 第一次 draw。通知、深链、支付、扫码和搜索等入口的内容可用点不同,需要分别定义路径;若没有调用报告 API,动态启动评估缺少可信的业务完成边界。
从 0-100 分到工程优先级
分数本身不能直接排期。可执行的转换方式是把评分项分成配置、测试、trace 和平台四类队列。
| 队列 | 进入条件 | 处理顺序 | 交付物 |
|---|---|---|---|
| 配置项 | 静态评分缺失或 CI 可自动识别 | AGP / R8 → Baseline Profile → Startup Profile → Compose / reportFullyDrawn() | 合并请求(MR)、构建报告、profile 覆盖清单 |
| 测试项 | 动态评分没有稳定脚本或路径覆盖不足 | 冷启动 → 通知启动 → 首页滚动 → 核心交易路径 → 动画 / 全屏路径 | Macrobenchmark 用例、设备列表、结果 JSON、trace 文件 |
| trace 项 | 动态分数低且单靠指标无法归因 | 固定场景 → 采 trace → 标注时间区间 → SQL 量化 → 归因到线程、I/O、Binder、GPU 或资源 | Perfetto 证据、SQL、截图或时间戳 |
| 平台项 | 分数反复波动或线上指标无法解释 | 端侧采集 → 上报 → 聚合 → 版本 / 设备 / 场景分组 → 告警 | APM 字段、看板、报警、灰度规则 |
静态分和动态分都偏低时,官方建议先改善静态项,因为配置修正也可能提升动态表现。之后用 Macrobenchmark 固定启动和渲染路径;复现稳定且数据仍然偏离预算时,再进入 trace 定位。若动态问题会阻断核心业务,也不能因为静态项尚未全部完成而延后处理。
高分仍可能伴随评分表未覆盖的风险。当前页面明确说明这是 App Performance Score 的第一版,评分、评估与建议以后可能变化。版本报告应保存评分日期、页面版本或规则快照,避免把不同规则生成的分数直接画在同一条趋势线上。
和 Android Vitals / Play Console 的关系
Android Vitals 反映 Play 用户的线上质量,App Performance Score 反映研发评估表覆盖的改进空间。两者可以建立指标映射,但不能强行统一字段、样本和时间窗口,更不能合成一个总分。
| 维度 | App Performance Score | Android Vitals / Play Console |
|---|---|---|
| 时间位置 | 发版前、专项中、回归测试中 | 发版后,Play 用户窗口内 |
| 数据来源 | 源码配置检查 + 物理设备动态测量 | 用户同意后由 Android 设备采集,Play Console / Reporting API 展示 |
| 主要指标 | 静态配置、启动到可交互、渲染 slow / frozen frames | crash、ANR、partial wake lock、启动、慢渲染、LMK、Slow Sessions 等 |
| 决策用途 | 找改进队列、评估专项收益、设置 CI 预算 | 判断线上坏行为、发版暂停、商店可见性风险 |
| 盲区 | 覆盖路径有限,设备组合有限 | 有窗口延迟,国内渠道和非 Play 分发覆盖不足 |
Android Vitals 官方说明覆盖稳定性、性能、电池和权限等问题。2026 年面向一般应用的核心指标(core vitals)包括用户感知崩溃率、用户感知 ANR 率,以及 excessive partial wake lock;Wear OS 表盘应用还包含 excessive battery usage(过度耗电)。
partial wake lock 会让 CPU 在屏幕关闭后继续运行,持续时间过长会造成额外耗电。部分阈值会影响 Google Play 可见性,具体阈值、设备类型和执行日期见 26.8;App Performance Score 不提供这些线上阈值。
发版前用 App Performance Score 与 benchmark 查出可预防问题,例如 release 未启用 R8、profile 未进入产物、受控设备上的启动或渲染回归;上线后用 Vitals 与自建 APM 判断用户是否受影响。实验室结果良好而线上指标恶化时,应按版本、设备、入口和用户路径比较 Play 分组、内部 APM、benchmark trace 与变更记录,不能用实验室分数否定线上数据。
质量门禁与回归防护
App Performance Score 接入门禁时,不能只保存总分。门禁需要保存评分规则版本、分项、构建产物、设备、路径、前置状态、原始结果、trace、负责人和处置动作。
| 门禁层级 | 检查项 | 失败处理 | 记录字段 |
|---|---|---|---|
| 合并前 | R8、AGP、profile 文件、Compose 版本、reportFullyDrawn() 标记 | 阻断或要求性能负责人批准 | commit、模块、失败项、豁免原因 |
| 周期性 CI | 冷启动、通知启动、首页滚动、核心页面动画 | 标记回归,生成统计与 trace 对比 | 设备、系统版本、应用版本、样本数、分布、trace 路径 |
| 发版前 | App Performance Score 静态 + 动态项、低端机组合 | 暂停发布或缩小灰度 | 分数、路径覆盖、低端机结果、未解决项 |
| 灰度中 | 自建 APM 指标、Vitals 早期信号、用户日志 | 控量、回滚、补丁或下架灰度 | 版本、渠道、设备、实验组、报警时间 |
| 发布后 | Vitals 当前窗口、趋势和设备分布 | 建专项或回退策略 | Play 指标定义、内部指标、责任模块 |
门禁可以允许豁免,但豁免需要负责人、依据、影响路径、用户占比、替代防护和到期时间。没有这些字段,分数只能形成一次性报告。
回归防护适合使用团队自己的性能预算,无须追求 App Performance Score 满分。启动预算可以包含 TTFD 分布与超预算比例,渲染预算可以包含卡顿(jank)、slow/frozen frame 分布和连续卡顿。门禁同时检查样本量、设备状态和统计不确定性;调整预算时,应提供同一构建产物的 trace、实验记录或明确的业务取舍。
常见误用边界
业务指标仍需单独监控。一个应用分数高,但支付页点击后等待很久、搜索首屏空白、消息通知进入会话慢,用户仍会觉得差。业务路径的可用时间、成功率和取消率要由 APM 与业务埋点记录。
动态评分只能指出受测路径偏慢,根因仍要由 trace 验证。Perfetto 或 Android Studio Profiler 可以检查线程运行、Runnable 排队、I/O、Binder、锁等待、GPU、SurfaceFlinger 和资源加载。长 slice(trace 时间线中带起止时间的任务片段)可能包含睡眠或等待,需结合 thread_state 判断 CPU 是否持续执行;详见 14.1、16.3 和 26.1。
设备分层也要单独设计。官方建议选择代表用户群体的设备,并提示低端设备能放大问题。只用一台旗舰机测量,会遗漏低速存储、低内存、高温和 OEM 调度差异。弱网属于业务路径的额外测试条件,当前 App Performance Score 动态评分没有把它列为独立类别。
专项判断需要更细的证据。R8、Baseline Profile、Startup Profile、Compose 版本这些静态项适合快速修正;数据库膨胀、图片解码、页面预加载、SurfaceView 合成、GPU 带宽和后台任务唤醒等问题则要另行测量。分数可以提示调查方向,无法列举全部根因。
App Performance Score 与 Android Performance Analyzer 联动
动态评分发现问题后,工具要按工作负载选择。Android Performance Analyzer 当前官方定位面向游戏性能分析,重点能力包括 Vulkan render pass(组织一组渲染操作的 Vulkan 单元)的调试标注,以及基于项目的多 trace 比较。
普通 Android App 的启动、UI 线程和 FrameTimeline 分析以 Perfetto 或 Android Studio 为主;包含 Vulkan 游戏渲染时,再使用 APA 的专用视图。
推荐路径如下:
| 评分异常 | 主要工具与观察点 | 后续动作 |
|---|---|---|
| TTFD 变慢 | Perfetto:app launch、main thread、Binder、I/O、CPU frequency、thread_state | 标注进程启动、首帧、内容可用与可交互点 |
| 滚动 slow frames 上升 | Perfetto:FrameTimeline、UI thread、RenderThread、SurfaceFlinger | 按帧区间判断 App、合成或 GPU deadline miss |
| Vulkan 游戏动画异常 | APA:Vulkan debug markers、render pass、多 trace 项目 | 固定场景并比较相同设备上的前后 trace |
| 低端机波动大 | Perfetto 与设备状态:频率、thermal、内存、I/O、后台负载 | 同一设备重复采样,量化环境差异 |
不论使用哪种查看器,报告都要保留原始 trace、关键时间戳、设备与构建信息、采集配置、查询语句和工具版本。截图只能辅助说明,不能替代可复查的 trace。
分数口径的团队协作模板
一次评分报告应该让研发、测试、产品都能读懂,但字段不能变成宣传稿。推荐模板如下:
| 字段 | 写法 |
|---|---|
| 测试对象 | 应用版本、commit(源码修订标识)、构建类型、是否启用代码压缩与优化(minify)、是否带 profile |
| 设备组合 | 设备型号、SoC、Android 版本、刷新率、存储剩余、温度起点 |
| 静态项 | AGP、R8、Baseline Profile、Startup Profile、Compose、TTFD 标记 |
| 动态路径 | 冷启动、通知启动、首页滚动、核心页面、动画或全屏路径 |
| 分数变化 | 总分、静态分、动态分、变化项,不只写总分 |
| 证据 | Macrobenchmark JSON、Perfetto 或 APA trace、截图、SQL、APM 链接 |
| 决策 | 立即处理、进入下个版本、限期豁免、无需处理 |
| 责任人 | 模块、负责人、截止日期、复测时间 |
报告应写清已完成项、仍超预算的路径、证据和后续动作。例如:“release 已启用 R8,产物已验证包含 Baseline Profile;低端设备 A 的冷启动分布仍超团队预算,trace 显示首页数据预加载占用主线程,进入 21.1 启动专项。”这种结论可以被分派和复测。
低端机样本池建设
动态评分离不开设备池。官方提醒低端设备能放大性能问题,也要求选择接近用户群体的设备。样本池不必很大,但要覆盖会改变结论的主要维度。
| 设备类型 | 覆盖目的 | 最小要求 |
|---|---|---|
| 低端机 | 放大启动、I/O、内存、线程调度问题 | 低内存、低存储、eMMC 或低速 UFS 存储、60Hz |
| 主流机 | 覆盖主要用户群体体验 | 当前线上占比最高的 SoC / OEM 组合 |
| 高刷机 | 检查刷新率变化下的帧表现 | 记录运行时刷新率与标称垂直同步(nominal VSync)间隔,不把固定 16.6ms 套给所有设备 |
| 低存储设备 | 复现安装、数据库、缓存、dexopt(DEX 安装后编译)和 I/O 波动 | 使用团队定义的低存储分组,并记录实际可用空间和清理策略 |
| 弱网场景 | 区分业务可用时间中的网络与本地执行 | 固定网络整形参数,即人为设定带宽、延迟和丢包;与 App Performance Score 分项报告 |
| 高温前后 | 检查热状态(thermal)对 CPU / GPU 频率和动态分的影响 | 同场景冷机、热机各跑多轮 |
每台设备都要绑定维护规则:系统版本、刷新率、供电、后台状态、编译模式、缓存/数据处理、可用存储和温度起点都要记录。官方也提醒动态分数可能在代码未变时波动;应连续运行多轮并报告常见表现与分布。环境字段缺失时,评分只能作为线索。
Android 17 的 CPU/GPU headroom 边界
CPU/GPU headroom 是处理器与图形处理器的剩余容量估计,独立于 App Performance Score、Play 和自建 APM 的指标。Android 16(API 36)加入这项能力,适合在重复、持续且负载较高的场景中作为 trace 与 benchmark 的环境旁证。
Android 17 通过 SystemHealthManager 提供公开查询入口;PerformanceHintManager 用于另一类运行时协作。
android-17.0.0_r1 的 SystemHealthManager.java 暴露 getCpuHeadroom() 与 getGpuHeadroom()。有效结果为 0~100,0 表示当前估算没有更多容量;暂时无法估算时可能返回 Float.NaN,设备不支持时抛出 UnsupportedOperationException。
调用方还应读取设备支持的计算窗口、CPU TID(线程 ID)数量上限与最小轮询间隔,不能把一套参数固定到所有设备。
headroom 侧重近期历史负载,无法预测未来性能。AOSP 注释所说的 TOCTOU,指查询与采取动作之间系统状态已经变化:快速调度和动态调频可能让刚读到的余量迅速失效,因此不宜每次轮询都激进调整工作负载。热限制或电源控制降低频率时,相同工作量的容量占比也会变化。它适合在同一设备、同一路径、相近温度和供电状态下辅助比较;单次读数不足以定义“低端机”,CPU 或 GPU 的单项值也不足以判定根因。
每次有效查询至少包含一次同步 Binder 调用,也就是调用线程要等待系统服务返回的跨进程请求。源码说明这一步可能超过 1ms,首次查询或参数变化后还可能更慢。不要在 UI、RenderThread 或受测关键区间同步等待;采集器需保留 unsupported、NaN 与参数信息,禁止把缺失值记成 0。
PerformanceHintManager 提供性能提示 API。它从 Android 12(API 31)开始允许 App 为周期性工作创建 hint session;这种会话把执行同一周期任务的一组长期线程视为整体,App 每轮报告目标与实际工作时长,系统据此在期限和功耗之间调节资源。
Android 17 实现见 PerformanceHintManager.java。CPU/GPU headroom 查询属于 SystemHealthManager。
隐藏的 AIDL(Android 接口定义语言,用于声明跨进程接口)、测试专用 hint 常量和 HAL(硬件抽象层)内部方法均不属于普通 App 可依赖的稳定接口。
把这两套 API 放进性能报告时,应分清“测量”和“调节”:SystemHealthManager 的 headroom 是可选环境样本,PerformanceHintManager 是运行时协作机制。二者都不会自动提高 App Performance Score,评分改善仍需由同一构建产物、同一设备和同一路径的动态结果验证。
全文小结
发版质量门禁把候选包、测试环境、指标定义、发布状态和处置动作绑定到同一份证据记录。候选包阶段使用接近生产配置的构建与物理设备识别可重复退化;Android 17 兼容性按“保留原 targetSdk 运行”和“目标 API 37”两轮验证;灰度阶段同时观察质量指标和数据健康度;异常发生后根据适用范围选择配置回退、暂停版本或发布修复包。Performance Score 可以把静态配置和受测路径汇总为工程检查入口,但必须能展开到具体分项、设备、路径、原始结果和 trace,不能用总分覆盖局部退化或替代线上指标。
门禁不能消除发布风险。它应让剩余风险、数据不确定性、审批责任和停止条件可复核,并把本次事故暴露的场景与设备群加入下一次发布验证。
参考资料
- AOSP Android 17:
android-17.0.0_r1source manifest - 迁移应用到 Android 17
- Android 17:影响所有应用的行为变化
- Android 17:target API 37+ 的行为变化
- Android 应用兼容性框架
- 在 CI 中运行 Android Benchmark
- Macrobenchmark 指标
- Android vitals
- Google Play staged rollout
- Google Play Developer API:APKs 与 Tracks
- Google Play Developer API:
edits.tracks - Google Play:暂停已全量发布的版本
- AndroidX
TraceSectionMetricAPI 参考