与大多数 Android UI 测试不同,Macrobenchmark 测试在独立于应用本身的进程中运行。这对于实现停止应用进程以及将 DEX 字节码编译为机器码等功能是必要的。
您可以使用 UIAutomator 库或能够从测试进程控制目标应用的其他机制来驱动应用的状态。您不能将 Espresso 或 ActivityScenario 用于 Macrobenchmark,因为它们需要在与应用共享的进程中运行。
以下示例通过资源 ID 查找 RecyclerView 并向下滚动多次
Kotlin
@Test
fun scrollList() {
benchmarkRule.measureRepeated(
// ...
setupBlock = {
// Before starting to measure, navigate to the UI to be measured
val intent = Intent("$packageName.RECYCLER_VIEW_ACTIVITY")
startActivityAndWait(intent)
}
) {
val recycler = device.findObject(By.res(packageName, "recycler"))
// Set gesture margin to avoid triggering gesture navigation
// with input events from automation.
recycler.setGestureMargin(device.displayWidth / 5)
// Scroll down several times
repeat(3) { recycler.fling(Direction.DOWN) }
}
}
Java
@Test
public void scrollList() {
benchmarkRule.measureRepeated(
// ...
/* setupBlock */ scope -> {
// Before measuring, navigate to the UI to be measured.
val intent = Intent("$packageName.RECYCLER_VIEW_ACTIVITY")
scope.startActivityAndWait();
return Unit.INSTANCE;
},
/* measureBlock */ scope -> {
UiDevice device = scope.getDevice();
UiObject2 recycler = device.findObject(By.res(scope.getPackageName(), "recycler"));
// Set gesture margin to avoid triggering gesture navigation
// with input events from automation.
recycler.setGestureMargin(device.getDisplayWidth() / 5);
// Fling the recycler several times.
for (int i = 0; i < 3; i++) {
recycler.fling(Direction.DOWN);
}
return Unit.INSTANCE;
}
);
}
您的基准测试不必滚动界面。例如,它可以运行动画。它也不需要专门使用 UI Automator。只要视图系统(包括由 Jetpack Compose 产生的帧)正在生成帧,它就会收集性能指标。
导航到应用的内部部分
有时,您可能希望对应用中无法直接从外部访问的部分进行基准测试。例如,这可能是访问标记为 exported=false 的内部 Activity、导航到 Fragment,或者滑动移除 UI 的某些部分。基准测试需要像用户一样手动导航到应用的这些部分。
要手动导航,请更改 setupBlock{} 中的代码以包含您想要的效果,例如轻触按钮或滑动。您的 measureBlock{} 仅包含您实际想要进行基准测试的 UI 操作
Kotlin
@Test
fun nonExportedActivityScrollList() {
benchmarkRule.measureRepeated(
// ...
setupBlock = setupBenchmark()
) {
// ...
}
}
private fun setupBenchmark(): MacrobenchmarkScope.() -> Unit = {
// Before starting to measure, navigate to the UI to be measured
startActivityAndWait()
// click a button to launch the target activity.
// While we use button text here to find the button, you could also use
// accessibility info or resourceId.
val selector = By.text("RecyclerView")
if (!device.wait(Until.hasObject(selector), 5_500)) {
fail("Could not find resource in time")
}
val launchRecyclerActivity = device.findObject(selector)
launchRecyclerActivity.click()
// wait until the activity is shown
device.wait(
Until.hasObject(By.clazz("$packageName.NonExportedRecyclerActivity")),
TimeUnit.SECONDS.toMillis(10)
)
}
Java
@Test
public void scrollList() {
benchmarkRule.measureRepeated(
// ...
/* setupBlock */ scope -> {
// Before measuring, navigate to the default activity.
scope.startActivityAndWait();
// Click a button to launch the target activity.
// While you use resourceId here to find the button, you can also
// use accessibility info or button text content.
UiObject2 launchRecyclerActivity = scope.getDevice().findObject(
By.res(packageName, "launchRecyclerActivity")
)
launchRecyclerActivity.click();
// Wait until activity is shown.
scope.getDevice().wait(
Until.hasObject(By.clazz("$packageName.NonExportedRecyclerActivity")),
10000L
)
return Unit.INSTANCE;
},
/* measureBlock */ scope -> {
// ...
}
);
}
推荐给您
- 注意:当 JavaScript 关闭时显示链接文本
- 编写 Macrobenchmark
- 捕获 Macrobenchmark 指标
- Microbenchmark