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:
- Run Macrobenchmark suite
- Regenerate baseline-prof.txt
- 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
- Instant Speed: 20–30% faster cold start
- Reduced Battery Usage: Less JIT work required
- Precise Control: AOT-compile only chosen code paths
Implementation Steps
- Add Macrobenchmark dependency
- Record key user flows
- Bundle generated baseline-prof.txt
- Integrate generation into CI for maintenance
References
- Android Baseline Profiles Overview
- AOT Compilation in Android Runtime
- JIT Compiler Flow
- Baseline Profile Gradle Plugin
- Creating Baseline Profiles
- Now in Android Baseline Profiles
- ProAndroidDev Article on Baseline Profiles
Related Articles
- Jetpack Compose: Architecture - Understanding the layered architecture of Jetpack Compose
- UI Layer with UDF Pattern - Implementing Unidirectional Data Flow in the UI layer
- Kotlin Coroutines: Dispatchers - Guide to coroutine dispatchers