configuring-r8-for-compose
Optimize R8 for Jetpack Compose to reduce APK size and improve startup performance.
Install
mkdir -p .claude/skills/configuring-r8-for-compose && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16497" && unzip -o skill.zip -d .claude/skills/configuring-r8-for-compose && rm skill.zipInstalls to .claude/skills/configuring-r8-for-compose
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use this skill to configure R8 correctly for a Jetpack Compose application — full mode by default, `proguard-android-optimize.txt`, resource shrinking on, and minimal keep rules because Compose ships consumer ProGuard rules. Covers AGP 8.0+ R8 full mode default, R8's Compose-aware optimizations (lambda grouping, `sourceInformation()` stripping, composable arg constant-folding, `ComposerImpl` devirtualization), legitimate keep needs (`@Serializable`, Hilt entry points, reflective `Saver`s), and the AGP 8.x missing-rule reporter / R8 retrace. Cited gain is roughly 75 percent startup and 60 percent frame-render improvement debug-to-release. Use when setting up a new Compose app, when a PR adds an over-broad keep like `-keep class androidx.compose.** { *; }`, when a release build crashes after enabling minification, when APK size needs reduction, or when first enabling minification.Key capabilities
- →Configure R8 for Jetpack Compose applications
- →Enable R8 full mode and resource shrinking
- →Use `proguard-android-optimize.txt` for R8 optimizations
- →Avoid over-broad keep rules for Compose components
- →Explain R8's Compose-aware optimizations
- →Verify R8 configuration with APK Analyzer and Macrobenchmark
How it works
The skill configures R8 for Compose by setting `isMinifyEnabled` and `isShrinkResources` to true, using `proguard-android-optimize.txt`, and advising against unnecessary blanket keep rules, use Compose's consumer ProGuard rules.
Inputs & outputs
When to use configuring-r8-for-compose
- →Configure R8 for Compose
- →Reduce APK size
- →Fix release build crashes
About this skill
Configuring R8 for Compose — Trust Consumer Rules, Avoid Blanket Keeps
R8 is the only supported shrinker — ProGuard is deprecated. Compose ships consumer ProGuard rules,
so most apps need no Compose-specific keep rules. The big modern wins come from R8 full mode (
default since AGP 8.0): lambda grouping, sourceInformation() stripping, constant-folding of
composable args, and ComposerImpl devirtualization. This skill teaches Claude to set R8 up
minimally and refuse over-broad keeps that defeat those wins.
When to use this skill
- Setting up a new Compose app's release variant for the first time.
- A PR adds
-keep class androidx.compose.** { *; }or any other Compose-wide keep — push back and apply this skill. - A release build crashes after enabling minification — the cause is almost always a missing keep on a reflective consumer, not on Compose itself.
- APK size review surfaces a large
classes.dexand the developer wants to know why. - The first time
isMinifyEnabled = trueis being flipped on, before any release benchmark is run. - Cross-link from
../../measurement/testing-compose-in-release-mode/SKILL.mdwhenever release measurement is being set up.
When NOT to use this skill
- The app is not yet ready to ship a release variant — wait until perf and crash budgets are in scope.
- The developer is debugging a code-level bug (logic error, race, wrong state). R8 issues manifest
as
ClassNotFoundException,NoSuchMethodError, or empty reflection results — not behavioral bugs. - The release variant has been measured and is fine — do not pre-emptively add keep rules.
- The perf concern is recomposition, layout phase, or stability — see the relevant
stability/orrecomposition/skills.
Prerequisites
- AGP 8.0+ for R8 full mode default. If on older AGP, full mode must be explicitly enabled in
gradle.propertieswithandroid.enableR8.fullMode=true. - A buildable release variant (debug-signed is fine while iterating — see
../../measurement/testing-compose-in-release-mode/SKILL.mdfor measurement signing). - Kotlin 2.0+ with the
org.jetbrains.kotlin.plugin.composeplugin so Compose's bundled consumer rules are wired correctly through dependency resolution. - Familiarity with the difference between
proguard-android.txt(no R8 optimizations) andproguard-android-optimize.txt(R8 optimizations on).
What R8 actually does for Compose (since AGP 8.0)
Surface these to the developer when they ask "why bother" or push back against minification:
- Lambda grouping. R8 merges many of Compose's generated lambda classes, reducing dex size and method count.
sourceInformation()stripping. Each composable emits a string of source-position metadata. R8 removes it from release builds — smaller APK and less work per recomposition.- Composable arg constant-folding. Constants that would have been wrapped by Live Literals in
debug are now folded back into
if (changed and 1 == 0)style checks the recomposer can short-circuit. ComposerImpldevirtualization. R8 devirtualizes hot calls into Compose's runtime, which is a major contributor to the cited frame-render improvement.- Resource shrinking. Paired with
isShrinkResources = true, R8 strips unreachable drawables, strings, and layout XML.
Cited measurement: roughly 75 percent startup gain and 60 percent frame-render gain debug to release. Source: Ben Trengrove, "Why should you always test Compose performance in release" (Android Developers Medium).
Workflow
- 1. Configure the release buildType. This is the entire happy-path config — no Compose-specific keeps needed:
// app/build.gradle.kts
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
}
-
2. Verify the default file name. The single most common mistake is using
proguard-android.txtinstead ofproguard-android-optimize.txt. Only the-optimizevariant enables R8's optimization passes. -
3. Trust Compose's bundled consumer rules. DO NOT add Compose-wide keeps. Each Compose artifact (
androidx.compose.runtime,androidx.compose.ui,androidx.compose.foundation,androidx.compose.material3, etc.) ships aconsumer-rules.prothat R8 picks up automatically. The developer'sproguard-rules.proshould contain no lines starting with-keep class androidx.compose.. -
4. Identify legitimate keep needs from the project. R8 problems are usually outside Compose:
@Serializabledata classes consumed bykotlinx.serialization(thekotlinx-serializationGradle plugin emits the right rules — verify, do not duplicate).- Hilt / Dagger entry points and generated DI code (the Hilt plugin emits these — verify).
- Custom
rememberSaveableSaverobjects only when accessed reflectively from outside the module. - Any
Class.forName(...)consumer, JNI binding, or service loader. - Public API surfaces of library modules consumed by other apps.
-
5. Run the AGP 8.x missing-rule reporter to find under-keeps. Do not add speculative keeps. After a build that fails at runtime in release, AGP writes a report at
app/build/outputs/mapping/release/missing_rules.txtlisting the exact-keepdirectives R8 inferred from runtime failures. Add only those, narrowed to the specific class/method. -
6. Use R8 retrace to map crash stacks back to source. When a release crash arrives, run the modern R8 retrace shipped with the Android command-line tools:
# Modern R8 retrace (cmdline-tools 7.0+):
$ANDROID_HOME/cmdline-tools/latest/bin/retrace \
app/build/outputs/mapping/release/mapping.txt < crash.txt
Or use the Gradle convenience target wired into AGP:
./gradlew :app:retraceR8DebuggingArtifact
(The legacy ProGuard retrace.sh install path under $ANDROID_HOME/tools/ was removed when SDK
Tools 26 was sunset; do not look for it.) Always retrace before diagnosing a release crash — the
stack frames are otherwise meaningless.
- 7. Verify with an APK analysis. After enabling minification:
./gradlew :app:assembleRelease
# Then in Android Studio: Build → Analyze APK → app-release.apk
# Compare classes.dex method count and resources.arsc size against the same build with isMinifyEnabled = false.
A correctly configured Compose app with no over-broad keeps typically halves the method count vs the un-minified release.
- 8. Re-run Macrobenchmark to capture the perf gain. Cross-link
../../measurement/testing-compose-in-release-mode/SKILL.md. The expected delta vsCompilationMode.Noneagainst a debug variant is in the 75 percent / 60 percent range cited above.
Patterns
Pattern: Over-broad Compose keep
# WRONG
-keep class androidx.compose.** { *; }
# WRONG because: Compose's own consumer rules are tighter and correct; a wildcard keep blocks lambda grouping, prevents sourceInformation stripping, kills composable-arg constant folding, defeats ComposerImpl devirtualization, and bloats classes.dex. The cited 75/60 gains evaporate.
# RIGHT — let Compose's consumer rules handle it.
# (no Compose-specific lines in proguard-rules.pro by default)
Pattern: Wrong default ProGuard file
// WRONG
buildTypes.release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro",
)
}
// WRONG because: proguard-android.txt explicitly skips R8's optimization passes. Lambda grouping, devirtualization, constant folding, sourceInformation stripping all do not run. The release variant builds and installs but performs like a poorly minified app.
// RIGHT
buildTypes.release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
Pattern: Defensive kotlin.Metadata keep
# WRONG
-keep class kotlin.Metadata { *; }
# WRONG because: blanket-keeping kotlin.Metadata blocks R8 from shrinking metadata for unreachable Kotlin classes; identify the actual reflective consumer (kotlinx.serialization, Moshi, Gson, Hilt) and add a narrow rule for that consumer instead.
# RIGHT — narrow to the reflective consumer
# Example: kotlinx.serialization needs Companion access on KSerializer implementations.
-keepclassmembers class * implements kotlinx.serialization.KSerializer {
public static **$Companion Companion;
}
Pattern: -dontobfuscate for "easier debugging"
# WRONG
-dontobfuscate
-dontoptimize
# WRONG because: turning off obfuscation and optimization on the release variant removes most of R8's benefit; the variant being measured is no longer the variant that ships. If readable stacks are needed, ship with R8 on and use retrace + the mapping.txt to deobfuscate.
# RIGHT — leave R8 on, keep mapping.txt for retrace.
# (no -dontobfuscate / -dontoptimize lines)
# Upload mapping.txt to your crash reporter (Crashlytics, Sentry, etc.) so stacks
# are deobfuscated automatically; or run R8 retrace manually:
# $ANDROID_HOME/cmdline-tools/latest/bin/retrace mapping.txt < crash.txt
# (or: ./gradlew :app:retraceR8DebuggingArtifact)
Pattern: @Serializable keep when the kotlinx-serialization plugin is missing
# RIGHT — when using kotlinx.serialization with reflection-based polymorphism
-keepclassmembers class * implements kotlinx.serialization.KSerializer {
public static **$Companion Companion;
}
-keepclasseswithmembers class * {
@kotlinx.serialization.Serializable <init>(...);
---
*Content truncated.*
When not to use it
- →When the app is not yet ready to ship a release variant
- →When debugging a code-level bug (logic error, race, wrong state)
- →When the release variant has been measured and is fine
Prerequisites
Limitations
- →Requires AGP 8.0+ for full mode default
- →Requires Kotlin 2.0+ with Compose plugin
- →Does not address recomposition, layout phase, or stability concerns
How it compares
This skill provides a specialized R8 configuration for Jetpack Compose, focusing on maximizing optimization by trusting consumer rules and avoiding blanket keeps, which is more effective than generic R8 setups.
Compared to similar skills
configuring-r8-for-compose side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| configuring-r8-for-compose (this skill) | 0 | 1mo | Review | Advanced |
| android-compose-performance | 0 | 4mo | Review | Intermediate |
| mobile-design | 149 | 4mo | Review | Intermediate |
| android-kotlin-development | 268 | 5mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
android-compose-performance
krutikJain
Profile and improve Compose recomposition, layout, scrolling, startup, and rendering performance in Android apps.
mobile-design
sickn33
Mobile-first design and engineering doctrine for iOS and Android apps. Covers touch interaction, performance, platform conventions, offline behavior, and mobile-specific decision-making. Teaches principles and constraints, not fixed layouts. Use for React Native, Flutter, or native mobile apps.
android-kotlin-development
aj-geddes
Develop native Android apps with Kotlin. Covers MVVM with Jetpack, Compose for modern UI, Retrofit for API calls, Room for local storage, and navigation architecture.
kotlin-multiplatform
vitorpamplona
Platform abstraction decision-making for Amethyst KMP project. Guides when to abstract vs keep platform-specific, source set placement (commonMain, jvmAndroid, platform-specific), expect/actual patterns. Covers primary targets (Android, JVM/Desktop, iOS) with web/wasm future considerations. Integrates with gradle-expert for dependency issues. Triggers on: abstraction decisions ("should I share this?"), source set placement questions, expect/actual creation, build.gradle.kts work, incorrect placement detection, KMP dependency suggestions.
android-kotlin
alinaqi
Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing
testing-android-code
bitwarden
This skill should be used when writing or reviewing tests for Android code in Bitwarden. Triggered by "BaseViewModelTest", "BitwardenComposeTest", "BaseServiceTest", "stateEventFlow", "bufferedMutableSharedFlow", "FakeDispatcherManager", "expectNoEvents", "assertCoroutineThrows", "createMockCipher", "createMockSend", "asSuccess", "Why is my Bitwarden test failing?", or testing questions about ViewModels, repositories, Compose screens, or data sources in Bitwarden.