Baseline Profiles are a powerful form of Profile-Guided Optimization (PGO) for Android applications.

What Are Baseline Profiles?

Baseline Profiles are a performance optimization technique that:

  • Records “hot” code paths (methods/classes that run first or most often)
  • Bundles with APK/AAB so ART can AOT-compile critical paths on install
  • Typically provides 20–30% faster cold start and snappier runtime performance

How They Work

1. Record Hot Paths

Use the Jetpack Macrobenchmark library to simulate key user flows:

@HiltAndroidTest
class BaselineProfileGenerator {
    @get:Rule
    val benchmarkRule = BaselineProfileRule()
 
    @Test
    fun startup() = benchmarkRule.collectBaselineProfile(packageName = "com.example.app") {
        pressHome()
        startActivityAndWait()
    }
 
    @Test
    fun mainNavigation() = benchmarkRule.collectBaselineProfile(packageName = "com.example.app") {
        pressHome()
        startActivityAndWait()
        // Navigate through main screens
        onView(withId(R.id.nav_home)).perform(click())
        onView(withId(R.id.nav_profile)).perform(click())
    }
}

2. Generate Profile

Macrobenchmark outputs baseline-prof.txt, listing hot methods/classes:

# {compiled-methods}
com/example/MainActivity onCreate(Landroid/os/Bundle;)V
com/example/data/UserDao insert(Lcom/example/data/UserEntity;)J

3. Bundle Profile

Place baseline-prof.txt in src/main/baselineProfiles/; the Gradle plugin (AGP 8.0+) automatically packages it:

plugins {
    id 'com.android.application'
    id 'androidx.baselineprofile'
}
 
android {
    // ...
}
 
dependencies {
    baselineProfile("androidx.benchmark:benchmark-baseline-profile-gradle-plugin:1.2.3")
}

4. AOT Compilation on Install

ART reads the profile at install time and compiles only the specified entries into native code, improving performance from the first launch.

Integration & Maintenance

Baseline Profile Gradle Plugin

The plugin automates profile generation and packaging:

./gradlew generateBaselineProfile

ProfileInstaller for Dynamic Delivery

For apps using Play Feature Delivery, use ProfileInstaller to fetch and install profiles at first launch:

dependencies {
    implementation("androidx.profileinstaller:profileinstaller:1.3.1")
}

CI Automation

Set up a CI job to:

  1. Run Macrobenchmark suite
  2. Regenerate baseline-prof.txt
  3. Enforce capture of new hot paths
# Example GitHub Actions workflow
name: Baseline Profile Generation
on: [push]
jobs:
  generate-profile:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Generate Baseline Profile
        run: ./gradlew generateBaselineProfile

APK Size Monitoring

Profiles add only a few KB to your APK. Monitor size changes:

# Compare APK sizes
./gradlew assembleRelease
ls -lh app/build/outputs/apk/release/app-release.apk

Special Considerations

Jetpack Compose

For Compose apps, ensure you record:

  • Initial composition
  • Common recomposition paths
  • Layout methods
@Test
fun composeNavigation() = benchmarkRule.collectBaselineProfile(packageName = "com.example.app") {
    pressHome()
    startActivityAndWait()
    // Record Compose-specific flows
    onNodeWithText("Home").performClick()
    onNodeWithText("Profile").performClick()
}

Kotlin Coroutines

Cover full coroutine paths to ensure ART compiles relevant suspend-function entry points:

@Test
fun coroutineFlows() = benchmarkRule.collectBaselineProfile(packageName = "com.example.app") {
    // Record coroutine-heavy operations
    onView(withId(R.id.refresh)).perform(click())
    // Wait for coroutines to complete
    Thread.sleep(1000)
}

Benefits

  1. Instant Speed: 20–30% faster cold start
  2. Reduced Battery Usage: Less JIT work required
  3. Precise Control: AOT-compile only chosen code paths

Implementation Steps

  1. Add Macrobenchmark dependency
  2. Record key user flows
  3. Bundle generated baseline-prof.txt
  4. Integrate generation into CI for maintenance

References