ほとんどの Android UI テストとは異なり、Macrobenchmark テストはアプリ自体とは別個のプロセスで実行されます。これはアプリプロセスの停止や DEX バイトコードからマシンコードへのコンパイルなどを行うために必要です。
UIAutomator ライブラリか、テストプロセスからターゲット アプリを制御できるその他のメカニズムを使用して、アプリの状態を変更できます。Espresso または ActivityScenario
はアプリと共有するプロセスでの実行を想定しているため、Macrobenchmark では使用できません。
次の例では、RecyclerView
を検出するためにそのリソース ID を使用し、何度か下にスクロールしています。
@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) }
}
}
@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
とマークされた内部アクティビティにアクセスする場合、Fragment
にナビゲートする場合、UI の一部をスワイプする場合などがあります。ベンチマークはユーザーのようにアプリのこのような部分に手動でナビゲートする必要があります。
手動でナビゲートするには、setupBlock{}
内のコードを変更して、ボタンのタップやスワイプなどの必要な効果を含めます。measureBlock{}
には、実際にベンチマークを行う UI 操作のみを含めます。
@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)
)
}
@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 -> {
// ...
}
);
}
あなたへのおすすめ
Macrobenchmark を作成する
Plan to create quality apps and features from the start by understanding best practices and requirements.
Macrobenchmark の指標をキャプチャする
Plan to create quality apps and features from the start by understanding best practices and requirements.
Microbenchmark
Plan to create quality apps and features from the start by understanding best practices and requirements.