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 模块跑起来,看看你的启动时间能省下多少毫秒吧!

相关推荐
crmscs22 分钟前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob24 分钟前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔29 分钟前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei99629 分钟前
flutter和Android动画的对比
android·flutter·动画
lxysbly2 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首3 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-19436 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
Jinkxs6 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&6 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
LDORntKQH6 小时前
基于深度强化学习的混合动力汽车能量管理策略 1.利用DQN算法控制电池和发动机发电机组的功率分配 2
android