<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged macrobenchmark</title>
<link>https://coderlegion.com/tag/macrobenchmark</link>
<description>Powered by Question2Answer</description>
<item>
<title>Benchmark Android User Journeys with Macrobenchmark</title>
<link>https://coderlegion.com/29723/benchmark-android-user-journeys-with-macrobenchmark</link>
<description>&lt;p&gt;Unit tests can prove that a ViewModel returns the right state, but they cannot measure the latency and frame behavior a user experiences across a sequence of screens and a real application process. To understand real-world responsiveness, you need to use Macrobenchmark with UI Automator to drive a complete interaction from process launch to the user-visible refreshed state, then analyze frame and milestone timings.&lt;/p&gt;
&lt;h3 id=&quot;understanding-macrobenchmark-scope&quot;&gt;Understanding Macrobenchmark Scope&lt;/h3&gt;
&lt;p&gt;The useful distinction in performance testing is scope. A unit test isolates a method or state transition in memory. A standard UI test checks user-visible behavior and assertions against functional correctness. A Macrobenchmark runs outside the target app process and measures a larger end-user interaction with controlled compilation and performance metrics.&lt;/p&gt;
&lt;p&gt;Measuring a complete journey offers visibility into performance issues that isolated tests miss. For example, a multi-screen order-entry flow involves several distinct phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Launching the application and waiting for the home screen.&lt;/li&gt;
&lt;li&gt;Opening an order-entry surface.&lt;/li&gt;
&lt;li&gt;Selecting a package and filling required fields.&lt;/li&gt;
&lt;li&gt;Submitting the form.&lt;/li&gt;
&lt;li&gt;Waiting for the success message.&lt;/li&gt;
&lt;li&gt;Waiting until the new item appears in the pending list.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Measuring these steps together reveals delays that occur after an API response, such as rendering a refreshed list or handling complex layout inflation during navigation. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/managing-concurrent-git-commits-during-automated-publishing/&quot;&gt;Managing Concurrent Git Commits During Automated&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;configuring-the-benchmark-target&quot;&gt;Configuring the Benchmark Target&lt;/h3&gt;
&lt;p&gt;To run a reliable macrobenchmark, your application must be configured as profileable rather than debuggable. A debuggable build includes performance overhead from the Java Virtual Machine debugger and JIT constraints that distort real-world timing. A profileable build disables debugging while still permitting the benchmarking tool to capture trace information and metric counters.&lt;/p&gt;
&lt;p&gt;In your app-level build file, you configure the profileable manifest property inside the release or benchmark build type. The snippet below shows how to configure a dedicated benchmark build variant:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-groovy&quot;&gt;android {
    buildTypes {
        create(&quot;benchmark&quot;) {
            initWith(buildTypes.getByName(&quot;release&quot;))
            signingConfig = signingConfigs.getByName(&quot;debug&quot;)
            matchingFallbacks += listOf(&quot;release&quot;)
            applicationIdSuffix = &quot;.benchmark&quot;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alongside the build configuration, the instrumentation test must drive the application using UI Automator selectors. The test runner launches the process, executes the touch and input actions, and measures the resulting frame metrics without altering app bytecode.&lt;/p&gt;
&lt;h3 id=&quot;driving-the-flow-with-ui-automator&quot;&gt;Driving the Flow with UI Automator&lt;/h3&gt;
&lt;p&gt;The benchmark test structure coordinates between the host test process and the target application process. Below is an example of a Macrobenchmark test class that launches an order-entry journey and measures the transition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@RunWith(AndroidJUnit4::class)
@LargeTest
public class OrderJourneyBenchmark {

    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    public fun benchmarkOrderSubmission() {
        benchmarkRule.measureRepeated(
            packageName = &quot;com.example.app.benchmark&quot;,
            metrics = listOf(FrameTimingMetric(), StartupTimingMetric()),
            compilationMode = CompilationMode.DEFAULT,
            iterations = 5
        ) {
            pressHome()
            startActivityAndWait()

            // Open order entry surface
            device.findObject(By.res(&quot;com.example.app&quot;, &quot;button_new_order&quot;))
                .click()
            device.wait(Until.hasObject(By.res(&quot;com.example.app&quot;, &quot;form_container&quot;)), 3000)

            // Fill fields and submit
            device.findObject(By.res(&quot;com.example.app&quot;, &quot;input_package_name&quot;))
                .text = &quot;Standard Package&quot;
            device.findObject(By.res(&quot;com.example.app&quot;, &quot;button_submit&quot;))
                .click()

            // Wait for success message and refreshed list
            device.wait(Until.hasObject(By.res(
                &quot;com.example.app&quot;, 
                &quot;text_success_banner&quot;
            )), 5000)
            device.wait(Until.hasObject(By.res(
                &quot;com.example.app&quot;, 
                &quot;list_pending_orders&quot;
            )), 5000)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;managing-side-effects-and-data-isolation&quot;&gt;Managing Side Effects and Data Isolation&lt;/h3&gt;
&lt;p&gt;Running realistic user flows introduces operational side effects that require careful management. A flow that submits forms or creates records will write durable data to your backend or local database. If left unmanaged, repeated benchmark runs accumulate test artifacts that bloat local storage or clutter backend environments.&lt;/p&gt;
&lt;p&gt;To maintain valid performance numbers, you should isolate test data using a dedicated staging environment, implement automated database cleanup routines after each iteration, or use disposable test fixtures. Never point an automated performance benchmark against a production database without strict isolation controls. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/safe-multi-environment-database-orchestration/&quot;&gt;Safe Multi Environment Database Orchestration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another common pitfall is relying on insufficient sample sizes. A single measured iteration does not yield a reliable distribution or regression baseline. Performance numbers vary due to thermal throttling, background system processes, and CPU frequency scaling. Increasing the iteration count and observing the median value helps filter out transient noise.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Measuring complete user journeys with Macrobenchmark bridges the gap between synthetic unit tests and real-world user experience. By combining profileable builds, UI Automator drivers, and proper data isolation, you can establish repeatable performance baselines that capture multi-screen rendering behavior and latency across your entire application flow.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29723/benchmark-android-user-journeys-with-macrobenchmark</guid>
<pubDate>Sun, 04 Oct 2026 12:02:47 +0000</pubDate>
</item>
</channel>
</rss>