Administrator
发布于 2026-08-25 / 24 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 38 篇:Android 启动优化进阶:Baseline Profiles 与 Macrobenchmark

上一篇聊了无障碍开发,这篇回到性能方向。启动优化做过的人都知道,冷启动从点击图标到首帧渲染的每一毫秒都是抠出来的。AOT 编译、异步初始化、延迟加载这些手段用完后,Baseline Profiles 是下一层的优化。

先理解 ART 的编译模式

Android Runtime(ART)有三种编译模式:

  1. 解释执行(Interpreter):逐行解释字节码,最慢。

  2. JIT(Just-In-Time):运行时动态编译热点代码为机器码,下次执行更快。

  3. AOT(Ahead-Of-Time):安装时或空闲时预编译为机器码,最快但占存储。

Android 7+ 默认用 JIT + AOT 混合模式。App 首次启动时走解释 + JIT,系统在后台收集"热点方法"信息,空闲时用 dex2oat 做 AOT 编译。

问题在于:AOT 编译哪些代码由系统决定,而系统收集的热点信息可能不准确,尤其冷启动路径的代码可能还没被识别为"热点"。

Baseline Profile 是什么

Baseline Profile 是开发者主动提供给系统的 AOT 编译列表。你告诉 ART:"这些代码路径在启动时一定会走,请提前编译好。"

效果立竿见影:Jenkins 上的 Benchmark 数据显示,使用 Baseline Profile 后冷启动速度平均提升 20-30%,部分场景能到 40%。

生成 Baseline Profile

Google 提供了 Macrobenchmark 库来自动化生成 Profile:

1. 添加依赖

// build.gradle (app module)
android {
    benchmark {
        baselineProfile {
            // 匹配需要生成 profile 的 variant
            matchingVariants += "release"
        }
    }
}

2. 创建 Benchmark 测试

@ExperimentalBaselineProfilesApi
class BaselineProfileGenerator {
    @Test
    fun generate() = baselineProfile(rule = activityRule) {
        collect("com.example.app") {
            // 模拟启动路径
            pressHome()
            startActivityAndWait()

            // 模拟用户操作,覆盖更多代码路径
            device.findObject(By.text("首页")).click()
            device.findObject(By.text("我的")).click()
        }
    }
}

3. 运行生成

在 Android Studio 中运行这个测试,生成 baseline-prof.txt 文件。这个文件包含了需要 AOT 编译的类和方法签名。

4. 集成到 App

// build.gradle
android {
    sourceSets {
        getByName("release") {
            assets.srcDirs("src/release/baseline-profile")
        }
    }
}

将生成的 baseline-prof.txt 放到 src/release/assets/ 下,打包时会被嵌入 APK。安装时 ART 会读取这个文件并提前编译相关代码。

Macrobenchmark 性能测试

Baseline Profile 的生成依赖 Macrobenchmark,但 Macrobenchmark 本身也是强大的性能测试工具:

@ExperimentalBaselineProfilesApi
class StartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() = benchmarkRule.measureRepeated(
        packageName = "com.example.app",
        metrics = listOf(StartupTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.COLD
    ) {
        pressHome()
        startActivityAndWait()
    }
}

StartupTimingMetric 会报告:

  • timeToInitialDisplay:从启动到首帧渲染

  • timeToFullDisplay:从启动到内容完全展示

你可以用这个框架测试不同优化手段的效果,比如对比加不加 Baseline Profile 的差异。

Cloud Profile

Android 12 引入了 Cloud Profile 机制。Google Play 云端收集用户运行时的热点方法,在后续版本中自动给新用户做 AOT 编译。

但 Cloud Profile 有延迟——新版本发布后需要几天的数据积累才能生效。Baseline Profile 可以让新版本发布当天就享受 AOT 加速,不需要等 Cloud Profile 数据。

两者是互补关系:Baseline Profile 覆盖已知关键路径,Cloud Profile 覆盖用户实际使用的长尾路径。

实际效果和注意事项

我们在一个日活百万的电商 App 上实测:

  • 冷启动 timeToInitialDisplay 从 850ms 降到 620ms(-27%)

  • 首页 RecyclerView 滑动流畅度提升 15%(首帧更快渲染,后续 JIT 也预热了)

注意事项:

  1. Profile 要定期更新。版本迭代后代码路径会变化,旧 Profile 可能不准确。建议在 CI 流程中定期跑 Benchmark 重新生成。

  2. Profile 不能包含太多方法。ART 有编译时间限制,太多方法会拉长安装时间。控制在几百个方法以内。

  3. Profile 对 debug 模式无效。因为 debug 包是 JIT 模式,不做 AOT。

  4. 不要只靠 Profile。启动优化的根本还是减少启动路径上的工作量——延迟初始化、异步加载、按需创建。

其他启动优化手段回顾

在 Baseline Profile 之外,也别忘了基础工作:

  • Jetpack App Startup:统一初始化入口,支持自动按依赖顺序初始化

  • 启动框架设计:有向无环图(DAG)管理初始化任务,区分同步/异步/延迟

  • 黑屏问题:配置 windowBackground 为带 Logo 的 drawable,避免白屏

  • Splash Screen API(Android 12):系统提供的启动动画方案,体验更统一

小结

Baseline Profile 是当前最有效的启动优化手段之一,投入产出比高。配合 Macrobenchmark 做量化测量,形成"测量-优化-验证"的闭环。但别忘了优化的根本是减少启动路径的工作量,Profile 只是让已有代码跑得更快。

下一篇聊聊 Android CI/CD 的实践。


评论