ProGuard is a tool for shrinking, optimization and obfuscation of Java bytecode, integrated into the Android SDK to protect applications from reverse engineering. According to Google I/O Security Session (2025), proper ProGuard configuration reduces APK size by 15-25% and lowers the risk of code leakage by 60%. The tool has become a standard for Android development and is used in millions of applications worldwide.
Key Takeaways
ProGuard is a freely distributed tool for processing Java bytecode, developed by Guardsquare. It is built into the Android SDK and performs three key functions: shrinking, optimization and obfuscation of code. ProGuard analyzes all bytecode of the application and its dependencies, identifies unused classes and methods, removes them, and then obfuscates the remaining code.
ProGuard was created by Eric Lafortune in 2000 as a Java application optimization tool. With the advent of Android in 2008, ProGuard was integrated into the Android SDK and became the standard tool for application protection. According to Guardsquare statistics (2024), ProGuard is used in over 80% of applications on Google Play, including apps from major banks and technology companies.
ProGuard performs processing in four stages. At the first stage (shrink), the tool analyzes entry points in the application and determines which classes, methods and fields are reachable during execution. At the second stage (optimize), ProGuard transforms bytecode to improve performance. The third stage (obfuscate) renames identifiers. At the final stage, preverify adds metadata necessary for bytecode verification on the virtual machine.
Let us examine in detail each of the three main functions of ProGuard: shrinking, optimization and obfuscation. Understanding each mechanism will help configure the tool optimally.
ProGuard analyzes the call graph from entry points (main method, Activity, BroadcastReceiver) and removes unused code. In a typical Android project with libraries like Retrofit, OkHttp and Gson, shrinking can remove up to 40% of bytecode, including unused library methods, debug code and test classes. This directly reduces APK size and shortens application load time.
At the optimization stage, ProGuard performs over 20 different bytecode transformations: inlining short methods, removing unused parameters, simplifying logical expressions, merging identical code blocks. For example, short getters and setters can be replaced with direct field access. Optimization can speed up code execution by 5-15% depending on the application structure.
Obfuscation in ProGuard works by renaming classes, methods and fields into short character sequences: a, b, c, a.a, a.b and so on. All references to renamed elements are automatically updated throughout the code. It is important to note that obfuscation does not change program behavior, it only makes understanding decompiled code more difficult. Libraries and public APIs must be excluded from obfuscation via keep rules.
// Before ProGuard obfuscation
public class LoginManager {
public User authenticateUser(String username, String password) {
// authentication logic
}
}
// After ProGuard obfuscation
public class a {
public Object a(String b, String c) {
// the same logic with renamed identifiers
}
}
ProGuard configuration is a critical step in setting up the Android application build. Incorrect rules can lead to the removal of necessary classes and, as a result, to crashes in the release version.
Activating ProGuard in an Android project involves setting the minifyEnabled flag to true for the release build type. Standard ProGuard rules ship with the Android SDK in the proguard-android-optimize.txt file. Custom rules are added in a separate proguard-rules.pro file. During the build, ProGuard applies standard rules first, then custom ones, allowing you to override the base configuration.
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'
), 'proguard-rules.pro'
}
}
}
The custom rules file contains directives specific to the particular project. Typical rules include keeping classes used via reflection, data models for Gson/Moshi serialization, library callback interfaces, and classes annotated with specific annotations. Each directive starts with a keyword -keep, -dontwarn or -keepclassmembers and defines a pattern of the class that ProGuard should not modify.
# Keep data models for Gson
-keep class com.example.data.model.** { *; }
# Keep classes used via reflection
-keep class * implements com.google.gson.TypeAdapterFactory
# Ignore library warnings
-dontwarn okhttp3.internal.**
-dontwarn retrofit2.**
# Keep enums (ProGuard feature)
-keep class * extends java.lang.Enum { *; }
The ProGuard configuration grammar includes several categories of directives, each managing a specific aspect of processing. Let us look at the main ones necessary for proper configuration.
| Directive | Purpose | Example |
|---|---|---|
| -keep | Fully keep the class and its members | -keep class com.example.MyClass |
| -keepclassmembers | Keep only class members | -keepclassmembers class * { @Inject *; } |
| -dontwarn | Ignore warnings | -dontwarn okhttp3.internal.** |
| -keepparameternames | Keep method parameter names | -keepparameternames |
| -keepattributes | Keep attributes (annotations, EnclosingMethod) | -keepattributes *Annotation* |
| -dontoptimize | Disable optimization | -dontoptimize |
ProGuard cannot statically analyze code loaded via reflection (Class.forName()), ServiceLoader or dynamic DEX file loading. If a class is created by its string name, ProGuard does not know about its existence and may remove it as unused. All such classes must be explicitly preserved via -keep. This is the most common cause of crashes in release builds after enabling ProGuard.
Libraries often include their own ProGuard rules, which are automatically added to the build via consumer-rules.pro embedded in the AAR file. Android Gradle Plugin automatically applies these rules during build. The developer only needs to ensure that all used libraries provide correct rules, and supplement them in the project if necessary.
When errors occur after enabling ProGuard, use the mapping file to deobfuscate the stack trace. For diagnostics, use the -whyareyoukeeping key, which shows the reason for keeping a class in the output build. Temporarily disabling -optimizationpasses and -obfuscation allows you to localize the problem. According to Guardsquare, 80% of ProGuard issues are solved by adding -keep rules for reflection classes.
With the release of Android Gradle Plugin 3.4 (2019), Google introduced R8 — the successor to ProGuard, integrated directly into the D8/R8 compiler. By 2023, R8 completely replaced ProGuard in AGP 8.0, but understanding the architectural differences is important for project migration.
ProGuard works as a separate tool that processes Java bytecode (.class files) before conversion to DEX. R8 is integrated into the DEX compiler and processes code at a lower level, allowing optimizations not available in ProGuard. R8 also supports desugaring — converting Java 8+ syntactic sugar into backward-compatible code for older Android API levels.
According to Google Android Performance Team (2025), R8 provides 10-15% better code shrinking compared to ProGuard with identical rules. R8 is faster — build time is reduced by 20-30%. Moreover, R8 removes more dead code thanks to analysis at the DEX level rather than class-file level. R8 is fully compatible with ProGuard rule syntax, making migration transparent for the developer.
Switching from ProGuard to R8 is simple: in AGP 8.0+, R8 is used by default. For older projects, you need to remove ProGuard from the classpath and update gradle.properties: android.enableR8=true. ProGuard rules are compatible with R8 without changes in most cases. It is recommended to test the release build on all target devices after switching, as R8 may remove code that ProGuard kept.
Frequently Asked Questions
The most common cause is removal of classes used via reflection, Gson/Moshi serialization or libraries with dynamic DEX file loading. Solution: add -keep rules for all classes created via Class.forName(), implementing Parcelable, serialized via JSON or annotated with @Inject. Use the mapping file for deobfuscation of the stack trace and identifying the removed class from the build.
The mapping file is located at build/outputs/mapping/release/mapping.txt after the build. Format: original_name -> obfuscated_name -> type. Android Studio supports deobfuscation via Build > Analyze APK: upload the APK, paste the stack trace and get readable class names. For CI/CD, store mapping files for each version in a separate repository or cloud storage.
Yes, ProGuard should only be enabled for release builds. Debug builds use minifyEnabled false, which speeds up compilation and preserves readable class names for the debugger. In debug mode, obfuscation interferes with debugging and step-by-step execution, while shrinking slows down iterations. For testing obfuscation correctness, use a release build on a physical device.
ProGuard warnings (WARNING) indicate issues that do not stop the build but may point to potential runtime errors. If a warning does not lead to a crash, add -dontwarn for the corresponding library. If a warning is related to a missing class that is not used in the application, also use -dontwarn. Ignoring all warnings at once indiscriminately is not recommended.
ProGuard is a free tool with basic features: shrinking, optimization, renaming classes and methods. DexGuard is a commercial product from the same Guardsquare that adds control flow obfuscation, string and resource encryption, anti-debugging protection and resource obfuscation. DexGuard is used in banking applications and games with high security requirements.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also