Kiểm soát ứng dụng của bạn từ Macrobenchmark (Điểm chuẩn vĩ mô)

Khác với hầu hết các thử nghiệm giao diện người dùng Android, thử nghiệm Macrobenchmark chạy trong một quy trình riêng biệt với ứng dụng. Đây là điều cần thiết để cho phép những hoạt động như dừng quy trình ứng dụng và biên dịch từ mã byte DEX sang mã máy.

Bạn có thể điều khiển trạng thái của ứng dụng bằng cách sử dụng thư viện UIAutomator hoặc các cơ chế có thể kiểm soát ứng dụng đích từ quá trình thử nghiệm. Bạn không thể áp dụng các phương pháp như Espresso hoặc ActivityScenario cho Macrobenchmark vì các phương pháp này cần phải chạy trong một quy trình chung với ứng dụng.

Ví dụ sau đây tìm RecyclerView bằng mã tài nguyên và cuộn xuống một vài lần:

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;
        }
    );
}

Quá trình đo điểm chuẩn của bạn không bắt buộc phải cuộn giao diện người dùng. Thay vào đó, quá trình này có thể chạy ảnh động hoặc dùng phương thức khác. Quá trình này cũng không cần phải sử dụng UI Automator; miễn là các khung được hệ thống xem tạo ra (bao gồm cả các khung do Jetpack Compose tạo ra), thì các chỉ số về hiệu suất sẽ được thu thập.

Đôi khi, bạn muốn đo điểm chuẩn cho những phần không truy cập trực tiếp được từ phía ngoài ứng dụng. Ví dụ: đó có thể là việc truy cập vào các Hoạt động bên trong (được đánh dấu bằng exported=false), điều hướng đến một Fragment hoặc vuốt phần nào đó trên giao diện người dùng sang một bên. Điểm chuẩn cần phải di chuyển đến các phần đó của ứng dụng "theo cách thủ công" như người dùng vẫn thường làm.

Bạn có thể thực hiện việc này bằng cách thay đổi nội dung bên trong setupBlock{} để chứa hiệu ứng mong muốn (nhấp vào nút, vuốt, v.v.). measureBlock{} của bạn chỉ chứa thao tác giao diện người dùng mà bạn thực sự muốn đo điểm chuẩn.

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