与大多数 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。相反,它可以运行动画,例如。它也不需要专门使用 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 关闭时,将显示链接文本
- 编写宏基准测试
- 捕获宏基准测试指标
- Microbenchmark