کنترل کردن برنامه از Macrobenchmark (نماها)

مفاهیم و پیاده‌سازی Jetpack Compose

برخلاف اکثر آزمایش‌های میانای کاربری Android، آزمایش‌های Macrobenchmark در فرایندی جداگانه از خود برنامه اجرا می‌شوند. این کار برای فعال کردن مواردی مثل توقف فرایند برنامه و کامپایل کردن از بایت‌کد DEX به کد ماشینی لازم است.

می‌توانید وضعیت برنامه‌تان را بااستفاده از کتابخانه UIAutomator یا سایر سازوکارهایی که می‌توانند برنامه هدف را از فرایند آزمایش کنترل کنند، هدایت کنید. نمی‌توانید از Espresso یا ActivityScenario برای Macrobenchmark استفاده کنید زیرا این ابزارها انتظار دارند در فرایندی مشترک با برنامه اجرا شوند.

مثال زیر RecyclerView را بااستفاده از شناسه منبع آن پیدا می‌کند و چندین بار به پایین پیمایش می‌کند:

کاتلین

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

    }
}

جاوا

@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 Automator به‌طور خاص ندارد. تا زمانی که فریم‌ها تولید می‌شوند، سنجه‌های عملکرد را جمع‌آوری می‌کند.

گاهی اوقات می‌خواهید بخش‌هایی از برنامه‌تان را که مستقیماً از بیرون قابل‌دسترسی نیستند محک بزنید. برای مثال، این می‌تواند دسترسی به «فعالیت‌های» داخلی باشد که با exported=false علامت‌گذاری شده‌اند، پیمایش به Fragment، یا تند کشیدن بخشی از میانای کاربر. معیارها باید به‌صورت دستی به این بخش‌های برنامه پیمایش کنند، درست مثل یک کاربر.

برای پیمایش دستی، کد داخل setupBlock{} را تغییر دهید تا حاوی جلوه موردنظرتان باشد، مثلاً تک‌ضرب زدن روی دکمه یا تند کشیدن. measureBlock{} شما فقط حاوی دستکاری میانای کاربری است که می‌خواهید واقعاً محک بزنید:

کاتلین

@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()
    }
}

جاوا

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