Ali Mansour
Ali Mansour
Ali Mansour
Ali Mansour
Ali Mansour

Senior Software Engineer

Senior Mobile Engineer

Content Creator

Tech Speaker

Shrink Your App: Uncovering Redundancies with the Android R8 Analyzer

April 15, 2026 Code
Shrink Your App: Uncovering Redundancies with the Android R8 Analyzer

Have you ever wondered why your Android app size keeps growing, even when you haven’t added that many new features? Or maybe you’ve encountered a strange crash in a “Release” build that doesn’t happen in your “Debug” build, leading you to copy-paste a mysterious -keep rule from StackOverflow?

If you nodded along to either of those scenarios, this article is for you.

In this comprehensive guide, we are going to demystify R8, Android’s built-in code shrinker. We’ll explore why app size matters, how code shrinking actually works, the problem with traditional ProGuard rules, and finally, how to use the powerful R8 Configuration Analyzer to optimize your application like a pro.


1. What is R8 and Why Do We Need It?

Before we dive into the deep end, let’s establish some basic concepts.

When you write an Android app, you include your own code, but you also include third-party libraries (like Retrofit for networking, Glide for images, or Jetpack Compose for UI). These libraries are massive because they are built to handle every possible edge case for thousands of different apps.

However, your specific app probably only uses a tiny fraction of the code inside those libraries.

The “Tree Shaking” Analogy

Imagine your app is a tree. The trunk is your main application, and the branches are all the classes and functions you use. Many of the branches (the unused library code) are completely dead.

R8 is the tree shaker. During the release build process, R8 analyzes your entire codebase to figure out which parts of the code are actually being called. It then “shakes the tree,” causing all the dead, unused code to fall off.

This process is called Code Shrinking.

Alongside shrinking, R8 performs two other critical tasks:

  1. Obfuscation: It renames your classes and variables to short, meaningless names (like a, b, c). This reduces the file size further and makes it much harder for malicious actors to reverse-engineer your app.
  2. Optimization: It rewrites your code to be more efficient, sometimes entirely removing unnecessary intermediate steps (a process called “inlining”).

The result? A smaller, faster, and more secure APK!


2. The Danger of the “-keep” Rule

If R8 is so smart, why do release builds sometimes crash?

R8 analyzes your code statically. It looks for direct references (e.g., ClassA calls ClassB). But what happens if you use Reflection? Reflection is a technique where code looks up other code dynamically at runtime (often used by libraries like Gson or Moshi to parse JSON strings into Kotlin data classes).

Because R8 cannot always predict what reflection will do at runtime, it might accidentally “shake off” a class that you actually need. When the app runs and tries to find that class, it crashes with a ClassNotFoundException.

The “Sledgehammer” Approach

To prevent this, developers write ProGuard rules, specifically -keep rules. A -keep rule tells R8: “Hey, do not touch this code! Keep it exactly as it is, even if it looks unused.”

The problem is that writing good keep rules is hard. When faced with a crash and a looming deadline, a beginner might write something like this:

-keep class com.mycompany.app.models.** { *; }

Why is this bad?
This is the sledgehammer approach. This rule tells R8 to ignore the entire models package. If you have 50 unused data models in that package, R8 is forced to package all 50 of them into your final app. It cannot shrink them, and it cannot obfuscate them.

Over the years, as different developers come and go, an app’s proguard-rules.pro file fills up with these broad, overly aggressive “sledgehammer” rules. The app becomes bloated.


3. The Modern Solution: The R8 Configuration Analyzer

So, how do we clean up years of bad keep rules without breaking the app?

Enter the R8 Configuration Analyzer.

Starting with newer versions of the Android Gradle Plugin (AGP) and R8 (specifically R8 version >= 9.3.7-dev), Google introduced a way to generate quantitative data about exactly what your keep rules are doing.

Instead of guessing, the R8 Analyzer gives you a report card.

Step 1: Ensure Full Mode is Enabled

Before analyzing, ensure your app is using the best possible baseline. Historically, developers could disable “Full Mode” in R8 to avoid dealing with strict rules. Check your gradle.properties file. If you see this line:

android.enableR8.fullMode=false

Delete it. Full mode enables the most advanced tree shaking and optimizations. It is the modern standard, and disabling it is holding your app back.

Step 2: Generating the Analysis Data

To use the analyzer, you need to hook into the R8 build process. This involves running specific shell commands (usually utilizing Python and Protocol Buffers) to intercept R8’s internal mapping.

When you run a release build with the analyzer enabled, R8 will spit out a keep-rules.pb (protobuf) file. You then convert this into a readable JSON file.

# This is a conceptual example of running the analyzer tool
python3 r8_analyzer.py --input keep-rules.pb --output analysis.json

Step 3: Reading the Report Card

The analyzer will parse your proguard-rules.pro file and evaluate every single rule based on its Impact.

Scenario A: Redundant Rules

The analyzer might tell you a rule is Subsumed.
What does this mean? Many modern libraries (like Retrofit or OkHttp) now ship with their own consumer keep rules embedded inside them. If you wrote a manual rule 3 years ago for Retrofit, and Retrofit now handles it automatically, your manual rule is completely redundant.

Analyzer Output Example:

Warning: Rule -keep class retrofit2.** { *; } is subsumed by a library rule.
Action: You can safely delete this line from your local proguard-rules.pro.

Scenario B: High-Impact Broad Rules

The analyzer will flag “sledgehammer” rules by calculating their exact impact.

Analyzer Output Example:

Warning: Rule -keep class com.example.models.** { *; } has a High Impact.
Details: This rule is forcibly retaining 142 unused classes.
Action: Refine this rule!

How to refine it?
Instead of keeping the whole package, target only what you need. Better yet, use annotations. In your code, you can annotate the specific classes you need to keep:

import androidx.annotation.Keep

@Keep // This single annotation replaces a massive ProGuard rule!
data class UserResponse(
    val id: String,
    val name: String
)

By removing the broad package rule and using @Keep only where necessary, you instantly drop 140 unused classes from your final APK!


Conclusion

App optimization doesn’t have to be a guessing game.

By understanding how R8’s “tree shaking” works, avoiding the temptation to write broad sledgehammer -keep rules, and utilizing the data-driven insights of the R8 Configuration Analyzer, even beginner developers can dramatically shrink their application size.

A smaller app means faster downloads, happier users, and a more secure codebase. So open up that proguard-rules.pro file, generate an analysis, and start shaking that tree!

Tags:
Write a comment

Verified by MonsterInsights