Microbenchmark 用来测量应用中的小段代码。临时测一次,可以在本地工作副本中采用快速配置;准备长期跟踪性能时,应建立独立的基准测试模块,使测试配置与日常开发分开。
快速开始
快速配置不要求先把业务代码拆到其他模块。不过,为了使测量更接近发布构建,配置会关闭应用调试功能。请只在本地工作副本试用这些改动,不要把这一组临时配置提交到版本控制系统。
先在模块的 build.gradle.kts 或 build.gradle 中增加依赖。下面保留原文示例使用的 1.2.4,它是本例的固定版本,不表示当前最新版。
Kotlin DSL:
dependencies {
implementation("androidx.benchmark:benchmark-junit4:1.2.4")
}
Groovy:
dependencies {
implementation 'androidx.benchmark:benchmark-junit4:1.2.4'
}
此处使用 implementation,而不是 androidTestImplementation。按照这套快速配置,如果只使用后者,基准测试库的 manifest 不会合并到应用的 manifest,测试将无法正常运行。
接着将 debug 构建类型设为不可调试。示例中的 ... 表示其他已有配置,不能作为可执行语句照抄。
Kotlin DSL:
android {
...
buildTypes {
debug {
isDebuggable = false
}
}
}
Groovy:
android {
...
buildTypes {
debug {
debuggable false
}
}
}
把插桩测试运行器改为 AndroidBenchmarkRunner:
android {
...
defaultConfig {
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
}
}
Groovy 的对应写法:
android {
...
defaultConfig {
testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
}
}
在 androidTest 目录的测试文件中创建 BenchmarkRule。测试仍然是标准 Android 插桩测试,下面用 doSomeWork() 代表实际待测工作,项目需要自行定义这个函数并补齐相应导入。
Kotlin:
@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun benchmarkSomeWork() {
benchmarkRule.measureRepeated {
doSomeWork()
}
}
}
Java:
@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
@Rule
public BenchmarkRule benchmarkRule = new BenchmarkRule();
@Test
public void benchmarkSomeWork() {
final BenchmarkState state = benchmarkRule.getState();
while (state.keepRunning()) {
doSomeWork();
}
}
}
原文快速开始的 Java 示例使用 BenchmarkRuleKt.measureRepeated(...),但展示的调用缺少规则接收者,括号结构也多了一层闭合。本处改用原文后文已经给出的 BenchmarkState 循环,避免把那个不完整片段直接当作可编译示例。该调整依据 BenchmarkState 的官方接口说明。
为持续测试建立独立模块
长期使用时,把基准测试放入专门模块,让 debuggable=false 等设置与普通测试分开。如果普通测试也使用不可调试的模块,调试器和相关性能分析工具将无法照常附加到那些测试上。
Microbenchmark 会直接调用待测代码。因此,可以把可测量的代码抽到一个公共 Gradle 模块,再由应用和测试模块分别依赖它。原文模块关系图表达的是以下结构:
| 模块 | 职责与依赖 |
|---|---|
:benchmarkable |
保存可直接调用的待测代码 |
:app |
应用模块,依赖 :benchmarkable |
:microbenchmark |
基准测试模块,通过测试依赖引用 :benchmarkable |
Android Studio 的模块向导可以生成已经配置好的基准测试模块,包括基准测试目录和 debuggable=false。在 Project 面板中右键项目或模块,依次选择 New → Module,在模板面板选择 Benchmark,类型选择 Microbenchmark,将模块命名为 microbenchmark,最后点击 Finish。原文截图展示的是 Android Studio Bumblebee 的向导;界面布局可能随版本变化,上述模块类型和用途才是需要核对的信息。
如果需要在库模块的基准测试中启用代码缩减,将 android.buildTypes.release.androidTest.enableMinification 设为 true。这一能力要求 Android Gradle Plugin 8.3 或更高版本。
创建模块后,在其构建文件中添加对待测模块的测试依赖。实际模块名可以与示例不同。
Kotlin DSL:
dependencies {
// The module name might be different.
androidTestImplementation(project(":benchmarkable"))
}
Groovy:
dependencies {
// The module name might be different.
androidTestImplementation project(':benchmarkable')
}
创建 Microbenchmark 测试类
使用基准测试库提供的 BenchmarkRule。测量 Activity 时,可以配合 ActivityScenario 或 ActivityScenarioRule;需要在 UI 线程运行的被测代码可使用 @UiThreadTest。
Kotlin 版本:
@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun benchmarkSomeWork() {
benchmarkRule.measureRepeated {
doSomeWork()
}
}
}
Java 版本:
@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
@Rule
public BenchmarkRule benchmarkRule = new BenchmarkRule();
@Test
public void benchmarkSomeWork() {
final BenchmarkState state = benchmarkRule.getState();
while (state.keepRunning()) {
doSomeWork();
}
}
}
Kotlin 使用 measureRepeated,Java 示例通过 getState() 取得 BenchmarkState 并在 keepRunning() 循环中执行待测工作。迭代次数由库管理,不应擅自把它替换成任意固定次数的循环。
把准备工作移出计时区间
有些工作每轮都必须执行,却不属于想要测量的算法。例如就地排序会改变输入数组,下一轮应先恢复原始数据。Kotlin 可以通过 runWithTimingDisabled 暂停计时,在复制完成后继续测量排序。
以下保留原文的排序示例片段。它们依赖完整测试类中的字段、导入、排序实现和扩展函数;其中 ...、listToSort 及 SortingAlgorithms 不构成独立程序。完整上下文见官方 SortingBenchmarks.kt。
// using random with the same seed, so that it generates the same data every run
private val random = Random(0)
// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }
@Test
fun benchmark_quickSort() {
// ...
benchmarkRule.measureRepeated {
// copy the array with timing disabled to measure only the algorithm itself
listToSort = runWithTimingDisabled { unsorted.copyOf() }
// sort the array in place and measure how long it takes
SortingAlgorithms.quickSort(listToSort)
}
// assert only once not to add overhead to the benchmarks
assertTrue(listToSort.isSorted)
}
Java 通过 pauseTiming() 和 resumeTiming() 控制同一范围:
private final int[] unsorted = new int[10000];
public SampleBenchmark() {
// Use random with the same seed, so that it generates the same data every
// run.
Random random = new Random(0);
// Create the array once and copy it in benchmarks.
Arrays.setAll(unsorted, (index) -> random.nextInt());
}
@Test
public void benchmark_quickSort() {
final BenchmarkState state = benchmarkRule.getState();
int[] listToSort = new int[0];
while (state.keepRunning()) {
// Copy the array with timing disabled to measure only the algorithm
// itself.
state.pauseTiming();
listToSort = Arrays.copyOf(unsorted, 10000);
state.resumeTiming();
// Sort the array in place and measure how long it takes.
SortingAlgorithms.quickSort(listToSort);
}
// Assert only once, not to add overhead to the benchmarks.
assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
固定随机种子使每次测试生成相同的输入;每一轮复制数组,使排序算法不会反复处理已经排好序的数据。正确性断言放在循环外,对最后一次结果检查一次,避免在每轮测量中加入不必要的断言开销。
计时区间与暂停计时区间都应尽量精简。暂停计时只是从所报告的被测时长中排除准备工作,准备工作仍会执行并增加整次测试所需的时间。
运行测试
在 Android Studio 中,像执行其他 @Test 一样,点击测试类或方法旁编辑器边栏的运行按钮。原文运行截图的关键位置就是这个边栏入口。
也可以从项目目录执行指定模块的全部连接设备测试:
./gradlew benchmark:connectedCheck
仅运行一个测试方法:
./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork
这里沿用原文的 benchmark 模块名。如果前面采用向导默认示例名 microbenchmark,需要相应替换命令中的模块部分。包名、类名和方法名也必须与项目实际代码一致。
查看测量结果
成功执行后,Android Studio 会直接显示测量指标。完整的 JSON 报告还包含额外指标和设备信息;性能分析生成的 trace 文件及 JSON 报告会从设备复制到主机。原文结果截图用于说明结果出现的位置,并不提供可套用到其他设备的性能基线。
主机文件目录如下,其中各占位项应替换为实际项目、模块和设备信息:
project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/
设备上的 JSON 通常保存在测试 APK 的外部共享媒体目录,例如 /storage/emulated/0/Android/media/<app_id>/<app_id>-benchmarkData.json。比较结果时,要同时保留设备和构建配置。
处理配置错误
为了避免不准确的测量,库会检查环境是否适合评估接近发布状态的性能:
debuggable应为false。- 应使用物理设备;模拟器不适合作为这里的受支持测量环境。
- 已 root 的设备应锁定时钟频率。
- 设备电量至少为 25%。
检查失败时,库会报错。如果确实需要在受限配置下做一次探索性运行,可以把指定错误降为警告,让它们不再中断测试。通过插桩参数 androidx.benchmark.suppressErrors 传入以逗号分隔的错误名称即可。这不会修复导致测量偏差的环境条件。
Kotlin DSL:
android {
defaultConfig {
…
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
}
}
Groovy:
android {
defaultConfig {
…
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
}
}
这两个片段中的 … 都是省略标记,应保留原有配置,不应原样作为语句写入文件。
命令行也可传递该参数。原文命令写成 $ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY,其中属性前缀与 suppressErrors 均有拼写错误。根据 官方插桩参数说明,修正后是:
./gradlew :benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.androidx.benchmark.suppressErrors=DEBUGGABLE,LOW-BATTERY
上面的 $ 是原文的命令提示符,修正命令已经去掉。压制错误后,库会在测试结果名称前加入相应错误标记。例如可调试构建会添加 DEBUGGABLE_ 前缀,提醒读者该结果是在不理想的配置下取得的。
继续阅读
围绕启动、交互等较大范围的行为,可进一步阅读 Macrobenchmark;基准测试自身的性能分析与不使用 Gradle 的配置也见 Android Developers 的相关文档。
原文代码对照
下列两段保留原文写法,便于对照上文明确说明的修正。第一段 Java 示例结构不完整,第二段命令的参数存在原文拼写问题;两段均未在本机运行,不应直接当作可执行示例。
原文快速开始的 Java 片段
@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
@Rule
public BenchmarkRule benchmarkRule = new BenchmarkRule();
@Test
public void benchmarkSomeWork() {
BenchmarkRuleKt.measureRepeated(
(Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
);
}
}
}
原文错误抑制参数命令
$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY
来源与许可
原文:Write a Microbenchmark,Android Developers,页面标示更新日期为 2026-10-01。本文对正文作中文翻译,保留全部示例用途;修正了快速开始 Java 片段与命令行参数拼写,并将四幅界面或关系图的关键教学信息改写为文字及表格。
根据 Android Developers 的 Content License,除另有说明外,文档及其中展示的代码采用 Apache License 2.0。官方性能示例的版权声明为 Copyright 2021 The Android Open Source Project。代码按许可“按原样”提供,不附带明示或默示担保。Java 和 OpenJDK 是 Oracle 及/或其关联公司的商标或注册商标。











暂无评论内容