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

相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone5 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui