0
0
Android Kotlinmobile~15 mins

ProGuard and R8 optimization in Android Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - ProGuard and R8 optimization
What is it?
ProGuard and R8 are tools used in Android app development to make your app smaller and faster. They work by removing unused code, renaming parts of your code to shorter names, and optimizing the app's structure. This helps your app take less space on the device and run more efficiently. Both tools run during the build process before your app is ready to be installed.
Why it matters
Without ProGuard or R8, Android apps can be larger and slower because they include extra code that is never used. This wastes device storage and can make the app slower to start or use more memory. Optimizing your app helps users have a better experience, especially on devices with limited resources. It also helps protect your code by making it harder to read if someone tries to look inside your app.
Where it fits
Before learning ProGuard and R8, you should understand basic Android app structure and the build process using Gradle. After mastering these tools, you can explore advanced topics like app signing, multi-dex, and custom build configurations for release builds.
Mental Model
Core Idea
ProGuard and R8 act like smart editors that trim, rename, and polish your app's code to make it smaller and faster without changing how it works.
Think of it like...
Imagine packing for a trip: ProGuard and R8 are like expert packers who remove unnecessary items, fold clothes tightly, and label everything with short tags so your suitcase is lighter and easier to carry.
┌───────────────┐
│  Source Code  │
└──────┬────────┘
       │ Build with Gradle
       ▼
┌───────────────┐
│  ProGuard/R8  │
│ - Remove unused code
│ - Rename classes/methods
│ - Optimize bytecode
└──────┬────────┘
       │ Output
       ▼
┌───────────────┐
│ Optimized APK │
└───────────────┘
Build-Up - 7 Steps
1
FoundationWhat ProGuard and R8 Do
🤔
Concept: Introduce the basic purpose of ProGuard and R8 in Android apps.
ProGuard and R8 are tools that help reduce the size of your Android app and improve performance. They do this by removing code that your app never uses, renaming classes and methods to shorter names, and optimizing the code structure. R8 is the newer tool that combines these tasks and is now the default in Android builds.
Result
Your app's final file is smaller and runs more efficiently on devices.
Understanding the basic role of these tools helps you appreciate why build times include an optimization step and why release builds differ from debug builds.
2
FoundationHow to Enable ProGuard and R8
🤔
Concept: Learn how to activate these tools in your Android project.
In your app's build.gradle file, you enable code shrinking by setting 'minifyEnabled true' in the release build type. R8 runs automatically when minifyEnabled is true. You can also specify ProGuard rules files to customize what code to keep or remove.
Result
Your app build process includes code shrinking and optimization steps.
Knowing how to enable these tools is essential to start benefiting from code optimization and to prepare for customizing their behavior.
3
IntermediateUnderstanding ProGuard Rules
🤔Before reading on: do you think ProGuard removes all unused code automatically, or do you need to tell it what to keep? Commit to your answer.
Concept: Learn how to control what code ProGuard and R8 keep or remove using rules.
ProGuard rules are text files where you specify instructions like which classes or methods to keep, which to rename, or which to ignore. This is important because some code is used by reflection or external libraries and might look unused to the optimizer. Without rules, important code might be removed, causing your app to crash.
Result
You can safely shrink your app without breaking functionality by writing correct rules.
Understanding rules prevents common bugs where code needed at runtime is mistakenly removed, which is a frequent source of crashes in optimized apps.
4
IntermediateDifferences Between ProGuard and R8
🤔Before reading on: do you think R8 is just a new name for ProGuard, or does it do more? Commit to your answer.
Concept: Explore how R8 improves upon ProGuard and why it replaced it as the default tool.
R8 combines shrinking, obfuscation, and optimization in one step, making builds faster and producing smaller apps. It understands Kotlin better and integrates tightly with the Android build system. ProGuard is older and only does shrinking and obfuscation, requiring separate optimization steps.
Result
You know why modern Android projects use R8 and how it benefits your app.
Knowing the differences helps you troubleshoot build issues and choose the right tool or configuration for your project.
5
AdvancedHandling Reflection and Keep Rules
🤔Before reading on: do you think code used only via reflection is automatically kept or removed by default? Commit to your answer.
Concept: Learn how to protect code accessed by reflection from being removed by optimizers.
Code accessed by reflection is invisible to static analysis, so ProGuard and R8 might remove it if you don't tell them to keep it. You use '-keep' rules in your ProGuard file to specify classes, methods, or fields that must not be removed or renamed. This ensures your app works correctly when using reflection, serialization, or libraries like Gson.
Result
Your app avoids runtime crashes caused by missing code after optimization.
Understanding reflection's impact on optimization is key to writing effective keep rules and maintaining app stability.
6
AdvancedDebugging Optimization Issues
🤔Before reading on: do you think build errors always show exactly what code caused the problem? Commit to your answer.
Concept: Learn techniques to find and fix problems caused by ProGuard or R8 optimizations.
Sometimes your app crashes after optimization due to removed or renamed code. You can use mapping files generated by R8 to understand how names changed. Enabling verbose logging and using tools like 'retrace' help trace errors back to original code. You can also disable parts of optimization temporarily to isolate issues.
Result
You can fix optimization-related bugs and keep your app stable.
Knowing how to debug these issues saves time and frustration when your app behaves differently in release builds.
7
ExpertCustomizing R8 with Advanced Rules
🤔Before reading on: do you think R8 rules only control code removal, or can they also affect optimization and obfuscation? Commit to your answer.
Concept: Explore how to fine-tune R8 behavior beyond basic shrinking and obfuscation.
R8 rules can control not just what code to keep, but also how to optimize or rename it. You can specify aggressive optimizations, disable certain optimizations, or control how classes are merged. This level of control helps balance app size, performance, and debuggability. Advanced rules require careful testing to avoid breaking the app.
Result
You can tailor R8 to your app's unique needs for maximum benefit.
Understanding advanced rule customization unlocks expert-level control over app optimization and can lead to significant improvements.
Under the Hood
ProGuard and R8 analyze your app's compiled bytecode to find unused classes, methods, and fields. They build a graph of code usage starting from entry points like your app's main activities and libraries. Unreachable code is removed. Then, they rename classes and methods to short names to reduce size (obfuscation). Finally, they apply optimizations like inlining methods or merging classes to improve performance. R8 integrates these steps tightly and uses more advanced analysis techniques, especially for Kotlin code.
Why designed this way?
Android apps often include many libraries and features, leading to large app sizes. Early tools like ProGuard separated shrinking and optimization, which was slower and less efficient. R8 was designed to combine these steps for faster builds and better results. It also supports modern language features and integrates with the Android build system to simplify configuration and improve reliability.
┌───────────────┐
│ Compiled Code │
└──────┬────────┘
       │ Analyze usage graph
       ▼
┌───────────────┐
│ Remove unused │
│   code       │
└──────┬────────┘
       │ Rename classes/methods
       ▼
┌───────────────┐
│  Obfuscation  │
└──────┬────────┘
       │ Optimize bytecode
       ▼
┌───────────────┐
│  Optimized    │
│   Output     │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does enabling minifyEnabled always guarantee your app will be smaller? Commit to yes or no.
Common Belief:Turning on minifyEnabled always makes the app smaller without any extra work.
Tap to reveal reality
Reality:While minifyEnabled triggers shrinking, without proper ProGuard rules, important code might be kept unnecessarily or removed incorrectly, sometimes even increasing app size or causing crashes.
Why it matters:Assuming automatic shrinking works perfectly can lead to bigger apps or runtime failures, frustrating users and developers.
Quick: Do you think R8 and ProGuard produce identical outputs? Commit to yes or no.
Common Belief:R8 is just a replacement for ProGuard and produces the same results.
Tap to reveal reality
Reality:R8 produces different optimized outputs, often smaller and faster, and supports more advanced optimizations and Kotlin features that ProGuard does not.
Why it matters:Expecting identical behavior can cause confusion when migrating projects or debugging build issues.
Quick: If your app uses reflection, do you think ProGuard automatically keeps all reflected code? Commit to yes or no.
Common Belief:ProGuard and R8 automatically detect and keep all code used via reflection.
Tap to reveal reality
Reality:They cannot detect reflection usage automatically; you must specify keep rules to prevent removal of reflected code.
Why it matters:Missing keep rules for reflection leads to crashes that are hard to diagnose.
Quick: Does obfuscation improve app performance? Commit to yes or no.
Common Belief:Obfuscation mainly improves app speed by making code run faster.
Tap to reveal reality
Reality:Obfuscation mainly reduces app size and protects code readability; performance improvements come from optimization steps, not obfuscation itself.
Why it matters:Confusing obfuscation with optimization can lead to wrong expectations about app speed.
Expert Zone
1
R8's integration with Kotlin allows it to optimize inline functions and suspend functions better than ProGuard, which can significantly reduce app size in Kotlin-heavy projects.
2
The order of applying keep rules and optimization flags can affect the final app size and behavior, requiring careful rule management in complex projects.
3
Mapping files generated during obfuscation are essential for crash reporting tools to translate stack traces back to readable code, but managing these files securely is often overlooked.
When NOT to use
ProGuard and R8 are not suitable for debug builds where fast iteration and readable code are priorities. In such cases, minification and obfuscation should be disabled. Also, for very small apps or prototypes, the overhead of configuring these tools may not be worth the benefits.
Production Patterns
In production, developers use ProGuard/R8 with custom rules tailored to their app and libraries, integrate mapping files with crash reporting services like Firebase Crashlytics, and automate optimization in CI/CD pipelines. They also test release builds extensively to catch optimization-related bugs early.
Connections
Code Obfuscation
ProGuard and R8 implement code obfuscation as part of their process.
Understanding general obfuscation techniques helps grasp how these tools protect app code from reverse engineering.
Compiler Optimization
R8 performs bytecode optimization similar to compiler optimizations in other languages.
Knowing compiler optimization principles clarifies how R8 improves app performance beyond just shrinking code.
Data Compression
Code shrinking and obfuscation reduce app size like data compression reduces file size.
Recognizing parallels with compression helps understand trade-offs between size reduction and processing overhead.
Common Pitfalls
#1Removing code used by reflection without keep rules.
Wrong approach:-keep class com.example.MyClass { *; } // Missing keep for methods accessed by reflection
Correct approach:-keep class com.example.MyClass { public ; } // Keeps all public methods used via reflection
Root cause:Not realizing that reflection usage is invisible to static analysis causes important code to be removed.
#2Enabling minifyEnabled without testing release build thoroughly.
Wrong approach:buildTypes { release { minifyEnabled true // No testing or debugging } }
Correct approach:buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } // Test release build carefully
Root cause:Assuming release builds work the same as debug builds leads to missed runtime errors.
#3Using outdated ProGuard rules with R8 without updates.
Wrong approach:-dontoptimize // Disables optimizations unnecessarily in R8
Correct approach:// Update rules to leverage R8 optimizations // Remove -dontoptimize unless necessary
Root cause:Not updating rules when switching tools causes suboptimal builds and missed improvements.
Key Takeaways
ProGuard and R8 are essential tools that shrink, obfuscate, and optimize Android apps to improve size and performance.
R8 is the modern default tool that combines all optimization steps and supports Kotlin better than ProGuard.
Proper ProGuard rules are critical to prevent removal of code used by reflection or external libraries.
Debugging optimization issues requires understanding mapping files and how to trace obfuscated code.
Advanced customization of R8 rules allows fine control over app optimization but requires careful testing.