Sitemap

Mastering ProGuard in Android Multi-Module Projects: AGP 8.4+, R8, and Consumable Rules

4 min readMay 19, 2025

--

ChatGPT visualisation about Android ProGuard

1. Introduction + What is ProGuard?

When building Android apps, especially at scale, securing your code and reducing APK and bundle size becomes essential. This is where tools like ProGuard (and its modern replacement, R8) come into play.

ProGuard performs:

  • Shrinking: Removes unused classes and methods
  • Obfuscation: Renames classes, methods, and fields to meaningless names
  • Optimisation: Improves byte-code performance

⚠️ R8 vs ProGuard: R8 has replaced ProGuard as the default tool in Android Gradle Plugin (AGP) 3.4+. While R8 handles shrinking and obfuscation, it still uses ProGuard-compatible rules. You don’t need to install ProGuard separately.

This makes understanding and writing clean ProGuard rules more critical than ever, especially for teams dealing with multi-module architectures.

2. What Changed in AGP 8.4+ for ProGuard?

Android Gradle Plugin (AGP) 8.4 brought an important restriction: minifyEnabled true is now disallowed in library modules.

https://developer.android.com/build/releases/past-releases/agp-8-4-0-release-notes

Why? Because AGP has shifted the responsibility of code shrinking and obfuscation solely to the application module. The library modules should no longer perform minification on their own.

  • What to use instead? consumerProguardFiles(...)

Old (invalid in AGP 8.4+):

// ❌ This will fail
buildTypes {
release {
minifyEnabled true
}
}

New (valid):

android {
defaultConfig {
consumerProguardFiles(
"proguard-rules.pro",
"retrofit-proguard-rules.pro"
)
}
}

The rules you define in consumer-rules.pro or consumerProguardFiles(...) get merged into the application’s ProGuard rules during build time.

This change also aligns with how libraries are expected to behave: they shouldn’t make assumptions about shrinker settings, but they must communicate their requirements to consuming apps.

3. Understanding consumer-rules.pro

This file acts as a contract between a library and its consumers. If a library depends on reflection, uses annotations, or has classes that must not be stripped out, it should declare them here.

Some key use cases include:

  • Gson/Retrofit models that rely on annotations.
  • Glide modules with generated classes.
  • Keeping specific base interfaces or internal frameworks intact.
# Example
# For Retrofit & Gson
-keep class retrofit2.** { *; }
-keep interface retrofit2.Call
-keep class * {
@com.google.gson.annotations.SerializedName <fields>;
}

These rules ensure the consuming app retains required symbols after obfuscation.

4. Multi-Module ProGuard Architecture

If you have 5, 10, or even 20+ modules in a codebase, rule management can become chaotic. That’s where a clear architecture helps:

  • App module holds global rules and final minifyEnabled true config.
  • Library modules define only the required keep rules via consumerProguardFiles(...).
  • Third-party rules are organised and versioned.

Recommended Folder Structure:

app/
└── proguard-rules.pro
└── retrofit-proguard-rules.pro
└── glide-proguard-rules.pro

libraryA/
└── consumer-rules.pro
libraryB/
└── consumer-rules.pro

App’s build.gradle:

proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
"retrofit-proguard-rules.pro",
"glide-proguard-rules.pro"
)

5. Real-World ProGuard Rules

📦 Retrofit + Gson

Retrofit uses annotations and reflection. If you’re using @SerializedName, Gson’s parsing will break without the right ProGuard config.

-keep class retrofit2.** { *; }
-keep interface retrofit2.Call
-keepattributes Signature, RuntimeVisibleAnnotations
-keep class * {
@com.google.gson.annotations.SerializedName <fields>;
}

🌄 Glide

Glide generates code at compile time using annotations. Those generated classes must be preserved.

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl { *; }
-dontwarn okhttp3.**

6. Verifying Your Rules

To avoid runtime crashes, it’s important to test if your rules are being applied.

Add a dummy rule:

-checkdiscard class com.yourcompany.DummyProguardCheck

Build with info logs:

./gradlew :app:minifyReleaseWithR8 --info

Inspect output files:

  • mapping.txt: class name mappings
  • seeds.txt: retained classes
  • usage.txt: stripped out classes
  • configuration.txt: final merged ProGuard config

You can also use tools like APK Analyzer or R8 Visualizer to inspect results visually.

7. Conclusion

ProGuard and R8 are more than just code obfuscators. When used correctly in multi-module Android projects, they can:

  • Shrink your app size considerably
  • Obfuscate your business logic and deter reverse engineering
  • Maintain code safety across module boundaries

AGP 8.4 forces a more structured approach, and embracing consumer-rules.pro is now a necessity—not an option.

Key Takeaways:

  • Minification happens only in the app module.
  • Use consumerProguardFiles in libraries to define required rules.
  • Modularize your third-party rules to reduce duplication.
  • Regularly inspect merged rules to avoid accidental stripping.

💡 Pro Tip: Keep a test build variant like minifyDebug to test obfuscation during development.

Feel free to share this post with your team or peers — it might save someone hours of painful debugging. And if you have your own tips or practices around ProGuard, I’d love to hear them!

Thanks for reading 🙌

--

--

Dipen Jansari
Dipen Jansari

Written by Dipen Jansari

TL Mobile @FloBiz | Ex. ZebPay | Ex. OpenXcell

No responses yet