ほとんどの Android UI テストと異なり、Macrobenchmark テストはアプリ自体とは別個のプロセスで実行されます。これは、アプリプロセスの停止や、DEX バイトコードからマシンコードへのコンパイルなどを行うために必要です。
UIAutomator ライブラリか、テストプロセスからターゲット アプリを制御できるその他のメカニズムを使用して、アプリの状態を変更できます。Espresso や ActivityScenario
などのアプローチは、アプリと共有するプロセスでの実行を想定しているため、Macrobenchmark では機能しません。
次の例では、RecyclerView
を検出するためにそのリソース ID を使用し、何度か下にスクロールしています。
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 starting to measure, 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
とマークされている)にアクセスする場合、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 resourceId here to find the button, you could also use // accessibility info or button text content. val selector = By.res(packageName, "launchRecyclerActivity") 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 starting to measure, navigate to the default activity scope.startActivityAndWait(); // click a button to launch the target activity. // While we use resourceId here to find the button, you could 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 -> { // ... } ); }