Jetpack Macrobenchmark执行失败:By.res返回Null触发空指针异常
解决Jetpack Macrobenchmark中UiAutomator选择器空指针问题
问题场景
学习Jetpack Benchmark时,参考官方示例实现clickOnId方法,调用时传入"launchRecyclerActivity"触发空指针异常,报错显示找不到对应UI元素。
相关代码
- 自定义的
clickOnId方法:
private fun MacrobenchmarkScope.clickOnId(resourceId: String) { val selector = By.res(packageName, resourceId) if (!device.wait(Until.hasObject(selector), 2_500)) { fail("Did not find object with id $resourceId") } device .findObject(selector) .click() // Chill to ensure we capture the end of the click span in the trace. Thread.sleep(100) }
- 字符串资源定义:
<string name="launchRecyclerActivity">RecyclerView</string>
- 对应Compose组件:
BenchmarkActivityButton(name = "RecyclerView") { launchActivityWithTrace<NonExportedRecyclerActivity>() } @Composable fun BenchmarkActivityButton(name: String, onClick: () -> Unit) { TextButton( modifier = Modifier.padding(8.dp), onClick = onClick, border = BorderStroke(1.dp, MaterialTheme.colors.primary) ) { Text(name) } }
- 异常信息:
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.test.uiautomator.UiObject2.click()' on a null object reference at com.example.macrobenchmark.clickslatency.ClickLatencyBenchmark.clickOnId(ClickLatencyBenchmark.kt:189) at com.example.macrobenchmark.clickslatency.ClickLatencyBenchmark.access$clickOnId(ClickLatencyBenchmark.kt:40) at com.example.macrobenchmark.clickslatency.ClickLatencyBenchmark$simpleViewClick$2.invoke(ClickLatencyBenchmark.kt:64) at com.example.macrobenchmark.clickslatency.ClickLatencyBenchmark$simpleViewClick$2.invoke(ClickLatencyBenchmark.kt:51) at androidx.benchmark.macro.MacrobenchmarkKt$macrobenchmark$measurements$1$tracePath$1.invoke(Macrobenchmark.kt:193) at androidx.benchmark.macro.MacrobenchmarkKt$macrobenchmark$measurements$1$tracePath$1.invoke(Macrobenchmark.kt:167) at androidx.benchmark.perfetto.PerfettoCaptureWrapper.record(PerfettoCaptureWrapper.kt:90) at androidx.benchmark.macro.MacrobenchmarkKt.macrobenchmark(Macrobenchmark.kt:167) at androidx.benchmark.macro.MacrobenchmarkKt.macrobenchmarkWithStartupMode(Macrobenchmark.kt:301) at androidx.benchmark.macro.junit4.MacrobenchmarkRule.measureRepeated(MacrobenchmarkRule.kt:106) at com.example.macrobenchmark.clickslatency.ClickLatencyBenchmark.simpleViewClick(ClickLatencyBenchmark.kt:51)
核心原因
混淆了字符串资源的name和UiAutomator定位所需的View资源ID:传入的"launchRecyclerActivity"是字符串资源的name,但By.res()方法需要的是UI元素的唯一资源ID(不是显示文本对应的字符串资源)。当前Compose按钮没有设置可被UiAutomator识别的资源ID,导致选择器找不到元素,最终触发空指针。
解决步骤
1. 给Compose按钮设置可识别的定位标识
对于Compose组件,有两种常用方式让UiAutomator能定位到元素:
方式一:使用TestTag配合语义配置
在Compose按钮上添加testTag,同时开启testTagsAsResourceId,让TestTag被当作资源ID识别:
@Composable fun BenchmarkActivityButton(name: String, onClick: () -> Unit) { TextButton( modifier = Modifier .padding(8.dp) .semantics { testTagsAsResourceId = true // 开启TestTag作为资源ID testTag = "launchRecyclerActivity" // 设置和调用时一致的标识 }, onClick = onClick, border = BorderStroke(1.dp, MaterialTheme.colors.primary) ) { Text(name) } }
方式二:使用资源ID(推荐规范写法)
- 首先在
res/values/ids.xml中定义一个唯一的View资源ID:
<item name="btn_launch_recycler_activity" type="id"/>
- 然后在Compose组件中绑定该ID:
@Composable fun BenchmarkActivityButton(name: String, onClick: () -> Unit) { TextButton( modifier = Modifier .padding(8.dp) .semantics { testTagsAsResourceId = true testTag = stringResource(R.id.btn_launch_recycler_activity) }, onClick = onClick, border = BorderStroke(1.dp, MaterialTheme.colors.primary) ) { Text(name) } }
- 调用
clickOnId时传入这个ID的字符串形式:
clickOnId("btn_launch_recycler_activity")
2. 验证元素定位正确性
使用Android SDK自带的uiautomatorviewer工具(路径:Android SDK/tools/bin/uiautomatorviewer),连接设备后捕获当前界面,查看按钮元素的resource-id属性,确认是否和你设置的标识一致。
3. 优化等待时间(可选)
如果界面加载较慢,2500ms的等待时间可能不足以让元素出现,可以适当延长等待时长,比如改为5000ms:
if (!device.wait(Until.hasObject(selector), 5_000)) { fail("Did not find object with id $resourceId") }
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

