Now In Android 精讲 9 - Benchmark 与 Baseline Profile

写在前面

Jetpack Compose 应用在冷启动、导航首帧、滚动列表等场景中会受 JIT/AOT 状态影响------早期帧掉得越多,用户体感越差。本文带你了解 Now in Android 项目的 benchmarks 模块,聊聊 Baseline Profile 的原理、如何编写 Macrobenchmark,以及实际收益。


一、为什么要关心 Benchmark 与 Baseline Profile?

痛点 解法
冷启动慢、首帧卡顿 Baseline Profile 在安装期预编译关键路径
"感觉快了"没有数据支撑 Macrobenchmark 量化启动耗时、帧稳定性
JIT 暖机阶段体验差 预编译减少运行时 JIT 抖动

二、Baseline Profile 原理

2.1 工作流程

复制代码
生成 Profile ──▶ 合入 app 模块 ──▶ 安装期注入 ART ──▶ 部分 AOT 预编译
  1. Profile Installer (prod/release 构建默认启用)会在 app 安装阶段把 baseline-prof.txt 注入 ART Profile。
  2. ART 根据 Profile 对命中的类/方法做 Partial AOT,减少运行时 JIT 开销。
  3. 生成 Profile 依赖 Macrobenchmark 跑真机/模拟器场景,提取热点方法签名。

2.2 为什么能让 Compose 冷启动变顺?

  • 安装期预编译:关键路径提前优化,冷启动和首帧更稳。
  • 数据来源:来自真实或模拟的 CUJ(Critical User Journey),覆盖启动 + 渲染 + 交互。
  • 运行时收益:减少"前几秒越来越顺"的暖机阶段;首屏列表滚动更稳定;过渡动画掉帧更少。

三、ForYouBaselineProfile 示例拆解

代码位于 benchmarks/.../baselineprofile/ForYouBaselineProfile.kt

34:43:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/ForYouBaselineProfile.kt 复制代码
@Test
fun generate() =
    baselineProfileRule.collect(PACKAGE_NAME, maxIterations = 10) {
        startActivityAndAllowNotifications()
        forYouWaitForContent()
        forYouSelectTopics(true)
        forYouScrollFeedDownUp()
    }
要素 说明
BaselineProfileRule.collect 启动待测 app、执行 CUJ、收集方法调用频率
maxIterations = 10 最多跑 10 轮,收敛后提前结束
CUJ 设计 启动 → 等待内容 → 选话题 → 滚动,覆盖核心热点

小技巧:maxIterations 怎么选?

  • UI 稳定时:3--5 次即可,加快生成速度。
  • 复杂场景:可设 10,但注意设备发热导致指标漂移。
  • 多场景拆分:启动、滚动、详情页分开收集,合并到主 Profile,便于定位回归。

四、ForYouActions:CUJ 动作封装

ForYouActions.kt 封装了 For You 场景的 UIAutomator 操作,保证 CUJ 可重复、可读。

4.1 等待内容加载

28:36:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ForYouActions.kt 复制代码
fun MacrobenchmarkScope.forYouWaitForContent() {
    device.wait(Until.gone(By.res("loadingWheel")), 5_000)
    val obj = device.waitAndFindObject(By.res("forYou:topicSelection"), 10_000)
    obj.wait(untilHasChildren(), 60_000)
}
  • 二次确认:先等加载圈消失,再等列表有子项。
  • 长超时:最多 60s,降低网络抖动导致的失败。

4.2 选择话题

42:90:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ForYouActions.kt 复制代码
fun MacrobenchmarkScope.forYouSelectTopics(recheckTopicsIfChecked: Boolean = false) {
    val topics = device.findObject(By.res("forYou:topicSelection"))
    val horizontalMargin = 10 * topics.visibleBounds.width() / 100
    topics.setGestureMargins(horizontalMargin, 0, horizontalMargin, 0)
    // ... 循环选择至少 3 个话题
}
技巧 说明
setGestureMargins 规避系统边缘手势干扰
recheckTopicsIfChecked 多轮迭代时"点两次"刷新状态
childCount == 0 直接 fail 避免生成空 Profile

4.3 滚动列表

93:96:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ForYouActions.kt 复制代码
fun MacrobenchmarkScope.forYouScrollFeedDownUp() {
    val feedList = device.findObject(By.res("forYou:feed"))
    device.flingElementDownUp(feedList)
}

快速下滑再上滑,触发列表布局/绘制热点路径。

4.4 主题切换(可选)

98:108:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ForYouActions.kt 复制代码
fun MacrobenchmarkScope.setAppTheme(isDark: Boolean) {
    when (isDark) {
        true -> device.findObject(By.text("Dark")).click()
        false -> device.findObject(By.text("Light")).click()
    }
    device.waitForIdle()
    device.findObject(By.text("OK")).click()
    waitForObjectOnTopAppBar(By.text("Now in Android"))
}

覆盖不同主题下的布局代码路径。


五、StartupBenchmark:量化冷启动

StartupBenchmark.kt 衡量冷启动耗时,对比不同编译/Baseline Profile 状态下的收益。

5.1 四种编译模式

45:57:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt 复制代码
@Test fun startupWithoutPreCompilation() = startup(CompilationMode.None())

@Test fun startupWithPartialCompilationAndDisabledBaselineProfile() =
    startup(CompilationMode.Partial(baselineProfileMode = Disable, warmupIterations = 1))

@Test fun startupPrecompiledWithBaselineProfile() =
    startup(CompilationMode.Partial(baselineProfileMode = Require))

@Test fun startupFullyPrecompiled() = startup(CompilationMode.Full())
模式 说明 用途
None 纯 JIT,冷启动最慢 基线参照
Partial + Disable 部分 AOT,禁用 Baseline 隔离 Profile 差异
Partial + Require 部分 AOT + Baseline 真实发布态
Full 全量 AOT 极限参考

5.2 测量逻辑

59:74:benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt 复制代码
private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
    packageName = PACKAGE_NAME,
    metrics = BaselineProfileMetrics.allMetrics,
    compilationMode = compilationMode,
    iterations = 5,
    startupMode = COLD,
    setupBlock = {
        pressHome()
        allowNotifications()
    },
) {
    startActivityAndAllowNotifications()
    forYouWaitForContent()
}
参数 说明
iterations = 5 同一配置跑 5 次,提升统计可靠度
startupMode = COLD 冷启动,进程完全退出后重启
setupBlock 每轮前退到桌面、处理通知权限
结束条件 forYouWaitForContent() 等待内容就绪

六、实施步骤

bash 复制代码
┌─────────────────────────────────────────────────────────────┐
│  1. 准备环境                                                 │
│     Android Studio 最新版 + AGP 8+                          │
│     确保 Profile Installer 启用(默认 on)                   │
├─────────────────────────────────────────────────────────────┤
│  2. 生成 Baseline Profile                                    │
│     ./gradlew :benchmarks:pixel6api31aospBenchmark          │
│     产物位于 benchmark/build/outputs/baseline-prof.txt      │
├─────────────────────────────────────────────────────────────┤
│  3. 合入 app 模块                                            │
│     拷贝到 app/src/main/baseline-prof.txt                   │
├─────────────────────────────────────────────────────────────┤
│  4. 验证收益                                                 │
│     再跑 StartupBenchmark,对比各模式指标                    │
├─────────────────────────────────────────────────────────────┤
│  5. CI 集成                                                  │
│     放入可选 Job,大改动后跑一轮防回归                        │
└─────────────────────────────────────────────────────────────┘

七、收益预期

指标 优化前 优化后
冷启动 ~700--900ms ~400--600ms
首帧稳定 明显掉帧 更平滑
滚动流畅度 暖机阶段卡顿 首滚即顺

具体数值因设备/构建差异会有浮动,但趋势一致。


八、维护小技巧

  • 场景可重复:固定账号/数据,必要时禁用动画。
  • 迭代次数适中:冷启动 5--8 次可得稳定均值。
  • 分场景拆分:启动、滚动、详情页分测试,便于定位回归。
  • 定期再生成:功能演进会改变热点路径,Profile 需随版本更新。
  • 设备一致性:优先同型号真机。

结语

Baseline Profile + Macrobenchmark 是 Now in Android 这类 Compose 项目里性价比极高的性能优化手段:

  • 把"安装即顺畅"交付给用户
  • 把"可量化的性能"交给团队

快把 Benchmark 模块跑起来,看看你的启动时间能省下多少毫秒吧!

相关推荐
游戏开发爱好者82 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
王码码20352 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
黑码哥2 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
亓才孓3 小时前
[JDBC]元数据
android
独行soc3 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
金融RPA机器人丨实在智能3 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿3 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc3 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
王码码20354 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_915106324 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview