ควบคุมแอปจากการเปรียบเทียบแบบมาโคร

การทดสอบ Macrobenchmark แตกต่างจากการทดสอบ UI ของ Android ส่วนใหญ่ตรงที่กระบวนการที่แยกจากกัน จากตัวแอปเอง การดำเนินการนี้เป็นสิ่งจำเป็นสำหรับการเปิดใช้สิ่งต่างๆ เช่น การหยุด กระบวนการของแอปและการคอมไพล์จากไบต์โค้ด DEX ไปยังโค้ดของเครื่อง

คุณกำหนดสถานะของแอปได้โดยใช้ไลบรารี UIAutomator หรือ ที่สามารถควบคุมแอปเป้าหมายจากกระบวนการทดสอบได้ คุณไม่สามารถใช้ Espresso หรือ ActivityScenario สำหรับ การเปรียบเทียบมาโคร เนื่องจากลูกค้าคาดหวังว่าจะทำงานในกระบวนการที่แชร์กับแอป

ตัวอย่างต่อไปนี้จะค้นหา RecyclerView โดยใช้รหัสทรัพยากรและ เลื่อนลงหลายครั้ง

Kotlin

@Test
fun scrollList() {
    benchmarkRule.measureRepeated(
        // ...
        setupBlock = {
            uiAutomator {
                // Before starting to measure, navigate to the UI to be measured
                startIntent(Intent("$packageName.RECYCLER_VIEW_ACTIVITY"))
            }
        }
    ) {
        uiAutomator {
            val recycler = onElement { className == "androidx.recyclerview.widget.RecyclerView" }
            // 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 อัตโนมัติ โดยเฉพาะอย่างยิ่ง ระบบจะรวบรวมเมตริกประสิทธิภาพตราบใดที่ยังมีเฟรม ที่ผลิตโดยระบบมุมมอง รวมถึงเฟรมที่ผลิตโดย Jetpack Compose

บางครั้งคุณต้องการเปรียบเทียบส่วนต่างๆ ของแอปของคุณที่ไม่ได้ เข้าถึงได้จากภายนอก ตัวอย่างเช่น การเข้าถึงกิจกรรมภายใน ที่มีเครื่องหมาย exported=false, การไปยัง Fragment หรือการเลื่อน ของ UI บางส่วนออกไป การเปรียบเทียบต้องไปยังส่วนเหล่านี้ด้วยตนเอง เช่น ผู้ใช้

หากต้องการไปยังส่วนต่างๆ ด้วยตนเอง ให้เปลี่ยนโค้ดภายใน setupBlock{} ให้มี เอฟเฟกต์ที่ต้องการ เช่น การแตะปุ่มหรือเลื่อน measureBlock{} ของคุณมี เฉพาะการปรับแต่ง UI ที่คุณต้องการเปรียบเทียบเท่านั้น

Kotlin

@Test
fun nonExportedActivityScrollList() {
    benchmarkRule.measureRepeated(
        // ...
        setupBlock = setupBenchmark()
    ) {
        // ...
    }
}

private fun setupBenchmark(): MacrobenchmarkScope.() -> Unit = {
    uiAutomator {
        // Before starting to measure, navigate to the UI to be measured
        startApp(TARGET_PACKAGE)
        // click a button to launch the target activity.
        onElement { textAsString() == "RecyclerView" }.click()
        // wait until the activity is shown
        waitForStableInActiveWindow()
    }
}

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 -> {
            // ...
        }
    );
}