Yum News Yum News

Technology  ·  Artificial Intelligence  ·  Developer Insights

Breaking
Gemini Task Automation Guide: India UK Japan Apps ✦   How to Set Up ChatGPT Wallet for Agent Purchases ✦   How to Set Up and Use Gemini Live on Apple CarPlay ✦   How to Set Up Gemini Live on Apple CarPlay ✦   How to Use the Gemini Decisions API for Fast Choices ✦   How to Use Gemini Task Automation on Pixel Phones   ✦   Gemini 3.8 Live launches with native audio dialogue  ✦  Android September Drop adds Find Hub memory  ✦  ML Kit now supports Gemini Nano on-device inference
How to Generate R8 Keep Rules with Jetpack Keep
androiddevelopertutorialshow-to

How to Generate R8 Keep Rules with Jetpack Keep

Step-by-step guide to Jetpack Keep annotations that generate conditional R8 keep rules for reflection and JNI in Android apps.

R8 shrinks and obfuscates Android apps by removing unused code. Reflection and JNI calls break that process because R8 cannot always see the targets. Developers traditionally write separate keep rules files that stay out of sync with the code. Jetpack Keep moves those rules into annotations next to the reflective calls.

The Jetpack Keep Gradle plugin reads the annotations at build time and emits conditional keep rules for R8. Conditional rules keep a target only when R8 also keeps the calling code. Unused optional features therefore do not bloat the release APK.

Google introduced the library as an alpha at Android Dev Summit 2026 alongside other performance tools. Official documentation is available on the Android Developers site under app optimization.

Android developer reviewing code on a laptop

Requirements and setup

Your project needs Android Gradle Plugin 8.7 or higher. The library and plugin ship at the same version (currently 1.0.0-alpha01). Add them to your version catalog:

[versions]
annotationKeep = "1.0.0-alpha01"

[libraries]
androidx-annotation-keep = { group = "androidx.annotation.keep", name = "keep", version.ref = "annotationKeep" }

[plugins]
androidx-annotation-keep = { id = "androidx.annotation.keep", version.ref = "annotationKeep" }

Declare the plugin in the root build.gradle.kts without applying it, next to the Android plugin. Apply both the plugin and the dependency in every module that contains reflection or JNI.

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.androidx.annotation.keep)
}

dependencies {
    implementation(libs.androidx.annotation.keep)
}

Sync the project. For app modules the plugin registers a variant-specific ExtractKeepRules task. Library modules package the generated rules as consumer keep rules.

Annotate reflection calls

Place an annotation on the function, constructor, field, or class that performs the reflective call. The generated rule keeps the target only if R8 keeps the annotated element.

Four annotations cover the common cases:

  • @UsesReflectionToConstruct for getDeclaredConstructor().newInstance()
  • @UsesReflectionToAccessMethod for method lookups
  • @UsesReflectionToAccessField for field access
  • @UnconditionallyKeep for JNI or external entry points that R8 cannot see

Prefer classConstant when the class is on the compile classpath. Use className only for optional dependencies. Specify parameterTypes to keep a single overload; an empty array keeps only the no-argument constructor.

Example for constructing an implementation by name:

@UsesReflectionToConstruct(
    classConstant = StartupTask::class,
    parameterTypes = []
)
fun execute(className: String) {
    val task = Class.forName(className)
        .getDeclaredConstructor()
        .newInstance() as StartupTask
    task.run()
}

The generated rule keeps the no-argument constructor of every StartupTask implementation, but only while execute remains in the optimized app.

Use lint to find missing annotations

Jetpack Keep ships two lint checks. ReflectionWithoutKeepAnnotations flags reflective calls that lack a matching annotation. MissingKeepAnnotationPlugin flags modules that depend on the library but do not apply the plugin.

Run lint on the module:

./gradlew :app:lint

Apply automatic fixes:

./gradlew :app:lintFix

Review every added annotation. Lint inserts a TODO placeholder when it cannot determine the target class. Replace the placeholder before the code compiles.

Verify the generated rules

For an app module run the extraction task for the release variant:

./gradlew :app:releaseExtractKeepRules

The plugin writes rules to app/build/generated/release/androidx.annotation.keep.rules.pro. Each rule includes a context comment that points back to the annotated code. Compare the output with your existing manual rules before you delete them.

A package-wide manual rule may cover reflection that lint did not flag. Keep any rule that protects library or cross-module reflection.

Close-up of code on a computer screen

Tips for production use

Target the most specific class. Broader interfaces keep more members in subclasses and increase APK size.

Repeat the annotation when one function reflects on multiple targets. The plugin generates one conditional rule per annotation.

Test a full release build after you remove the old keep rules. R8 full mode and shrinking can surface previously hidden issues.

Pair Jetpack Keep with the R8 Configuration Analyzer to audit remaining manual rules. Our guide to auditing Android R8 keep rules shows how to run the analyzer and interpret its reports.

Conclusion

Jetpack Keep keeps R8 rules next to the code that needs them and generates conditional rules that shrink cleanly. Lint surfaces missing annotations, and the extraction task lets you inspect the output before you delete manual rules.

Start with one module that uses reflection. Annotate the calls, extract the rules, and compare them with your current proguard files. Once the release build stays stable, expand the annotations across the project.

Official sources:

Share this story