Android MacroBenchmark:FrameTimingMetric仅执行1次迭代失败求助
问题描述
尝试用FrameTimingMetric对RecyclerView执行5次滚动迭代测试,使用StartupTimingMetric的启动测试能正常完成5次迭代并生成结果,但滚动测试在第一次滚动后就抛出java.lang.IllegalArgumentException: Failed requirement错误,应用退出,剩余4次迭代无法执行。已确认基准模块配置正常,滚动测试中已等待RecyclerView加载完成(数据来自MarkMainActivity的OnCreate方法API请求),日志显示滚动操作已执行。
测试代码
@RunWith(AndroidJUnit4.class) public class ExampleStartupBenchmark { @Rule public MacrobenchmarkRule mBenchmarkRule = new MacrobenchmarkRule(); @Test public void startup() { mBenchmarkRule.measureRepeated( "my-app-package-name", Collections.singletonList(new StartupTimingMetric()), CompilationMode.DEFAULT, StartupMode.COLD, 5, scope -> { scope.startActivityAndWait(); return Unit.INSTANCE; }); } @Test public void scroll() { mBenchmarkRule.measureRepeated( "my-app-package-name", Collections.singletonList(new FrameTimingMetric()), CompilationMode.DEFAULT, StartupMode.COLD, 5, macrobenchmarkScope -> { Log.i("MacrobenchmarkRuleTAG", "justBeforeIntent"); Intent intent = new Intent("MarkMainActivity-intentlaunch-string"); macrobenchmarkScope.startActivityAndWait(intent); // Wait until activity is shown. Log.i("MacrobenchmarkRuleTAG", "justBeforeMarkMainActivityWait"); macrobenchmarkScope.getDevice().wait( Until.hasObject(By.clazz("MarkMainActivity")), 10_000L ); Log.i("MacrobenchmarkRuleTAG", "justAfterMarkMainActivityWait"); return Unit.INSTANCE; }, scope ->{ UiDevice device = scope.getDevice(); UiObject2 recycler = device.findObject(By.res(scope.getPackageName(), "mark_recyclerview")); SearchCondition<Boolean> searchCond2 = Until.hasObject(By.res("mark_adapter_single_view")); recycler.wait(searchCond2, 10_000L); // wait until recyclerview object loads a singleitemview Log.i("MacrobenchmarkRuleTAG", recycler.getClassName()); recycler.setGestureMargin(device.getDisplayWidth() / 5); Log.i("MacrobenchmarkRuleTAG", "justbeforefling"); recycler.scroll(Direction.DOWN, 55); return Unit.INSTANCE; }); } }
错误栈信息
java.lang.IllegalArgumentException: Failed requirement. at androidx.benchmark.macro.perfetto.FrameTimingQuery.getFrameSubMetrics(FrameTimingQuery.kt:182) at androidx.benchmark.macro.FrameTimingMetric.getMetrics$benchmark_macro_release(Metric.kt:231) at androidx.benchmark.macro.MacrobenchmarkKt.macrobenchmark(Macrobenchmark.kt:209) at androidx.benchmark.macro.MacrobenchmarkKt.macrobenchmarkWithStartupMode(Macrobenchmark.kt:301) at androidx.benchmark.macro.junit4.MacrobenchmarkRule.measureRepeated(MacrobenchmarkRule.kt:106) at com.tc.macrobenchmark.ExampleStartupBenchmark.scroll(ExampleStartupBenchmark.java:51)
解决方案
确保滚动操作产生足够有效帧
报错核心是FrameTimingQuery未捕获到足够有效帧数据,触发断言失败。当前recycler.scroll(Direction.DOWN, 55)滚动距离可能过短,或滚动后未等帧渲染完成就结束测试。建议改用fling操作(recycler.fling(Direction.DOWN)),或增加滚动距离;滚动后添加device.waitForIdle();,让系统完成帧渲染。调整启动模式
目前用StartupMode.COLD每次迭代冷启动,可能导致帧数据采集不稳定。可尝试改为StartupMode.WARM减少初始化开销;若必须冷启动,需确保每次迭代都完整等待RecyclerView数据加载完成,比如判断Item数量是否符合预期。升级Benchmark库版本
该错误可能是库的已知问题,建议升级androidx.benchmark:benchmark-macro到最新稳定版本,同时确保测试设备为Android 10及以上。优化等待逻辑准确性
现有等待MarkMainActivity的方式不够精准,建议改为等待RecyclerView可见且数据加载完成:// 等待RecyclerView可见 device.wait(Until.hasObject(By.res(scope.getPackageName(), "mark_recyclerview")), 10_000L); UiObject2 recycler = device.findObject(By.res(scope.getPackageName(), "mark_recyclerview")); // 等待列表至少有5个Item加载完成 recycler.wait(Until.findObjects(By.res("mark_adapter_single_view")).atLeast(5), 10_000L);避免空列表滚动导致无帧数据产生。
内容的提问来源于stack exchange,提问作者Bhavesh Aggarwal

