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 小时前
深入解析 SystemUI 依赖注入:Dagger2 与 Hilt 核心机制重温
android·dagger
從南走到北7 小时前
JAVA海外短剧国际版源码支持H5+Android+IOS
android·java·ios
霸王大陆7 小时前
《零基础学 PHP:从入门到实战》模块十:从应用到精通——掌握PHP进阶技术与现代化开发实战-1
android·开发语言·php
修炼者8 小时前
【Android 进阶】为什么你应该停止在 ViewModel `init` 中加载数据?
android
a3158238068 小时前
Android13隐藏某个App需要关注的源码文件
android·java·framework·launcher3·隐藏app
霸王大陆8 小时前
《零基础学 PHP:从入门到实战》模块十:从应用到精通——掌握PHP进阶技术与现代化开发实战-5
android·开发语言·php
AI视觉网奇8 小时前
android jni保存图片
android
私人珍藏库8 小时前
【安卓】Lightroom摄影师版PS滤镜免费
android·app·安卓·工具·软件