R8 & ProGuard — Guardians of the code

Image from wallpapersafari.com

Want to make your APK difficult to reverse engineer?

Want to develop the applications with the smallest possible size of the APK?

Want to detect and remove the unused parts of the code?

If your answers to these questions are YES, you are on the right place. In this article you will find out how to obfuscate, shrink and optimize your code using ProGuard rules.

How to do it?

Android Gradle Plugin with version bellow 3.4.0 was using ProGuard to perform compile-time code optimization. When project is built using Android Gradle plugin 3.4.0 or higher, R8 compiler is used by default to handle the following compile-time tasks::

  1. Minify the code (Shrink) — detects and removes unused classes, fields, methods and attributes from app and its library dependencies;
  2. Shrink resources — removes unused resources from the packaged app, including unused resources in the app’s library dependencies;
  3. Obfuscate the code — renames classes, fields, and methods via meaningless names, which results in reduced DEX file sizes;
  4. Optimize the code — optimizes bytecode and removes unused instructions. Do the things like inlining the functions.

These actions are very useful for making Android production-ready application. It helps you to reduce the size of the application, make app faster and make APK difficult to reverse engineering, which is especially valuable for app security.

Differences between R8 and ProGuard

ProGuard and R8 are quite similar. They both use the same configuration, so switching between them is easy. Both of them optimize Java bytecode, remove unused classes, fields, and methods, and optimize the method bodies for size and for performance.

But if you look deeper, there are some differences.

R8 is better at:

  • inlining container classes, thus avoiding object allocations.

ProGuard is better at:

  • simplifying enum types and propagating values,
  • creating more opportunities for optimization in subsequent steps.
  • offers more extensive support for backporting and more help with configuration problems. Its option ‘-addconfigurationdebugging’ provides invaluable feedback at run-time about possibly missing configurations.

Based on your needs and which shrinker ‘s characteristics are more important to you, you can choose shrinker for your project. Since in Android projects using Android Gradle plugin 3.4.0 or higher , R8 shrinker is used by default and if you want to use ProGuard instead, you need to add following code to ‘gradle.properties’:

In this article we will talk about using R8, but all the rules that apply to R8 also apply to ProGuard, so the developer can easily switch between them.

Enable shrinking, obfuscation, and code optimization in Android project

When you create a new project using Android Studio, shrinking, obfuscation, and code optimization is not enabled by default and in your build.gradle (Module:app) file there is default configuration:

That’s because these compile-time optimizations increase the build time of your project and might introduce bugs if you do not sufficiently customize which code to keep. The best practice is to keep shrinking, obfuscation, and code optimization disabled on debug mode and enable it on release mode.

To do that open build.gradle(Module:app) and include the following snippet:

  • minifyEnabled enables/disables shrinking, obfuscation, and code optimization (R8 shrinker is used by default)
  • proguardFiles includes the default ProGuard rules files that are packaged with Android Gradle plugin.

ProGuard tip for android libraries developers

If you have a library project that you use to build and publish an AAR, you can add a ProGuard configuration file to your library’s build configuration. Doing this you ensure that app modules which depend on your library don’t have to manually update their ProGuard files to use your library. In this case when Android Studio build system builds the app, it will use the merged directives from both, app module and the library.

To achieve it open build.gradle(Module:app) and include the following snippet:

ProGuard rules files

When you set the minifyEnabled property to true, R8 combines rules from all the available sources listed below.

Proguard rules files :

  1. The getDefaultProguardFile() refers default file “proguard-android.txt” which you can find in build/intermediates/proguard-files directory of your project. Instead of it you can also use “proguard-android-optimize.txt” file for more code shrinking located on the same folder.
  2. The “proguard-rules.pro” is file where you can add custom ProGuard rules. It is located at the root of the project module. By default, this file does not apply any rules.
  3. aapt_rules.txt file you can find in build/intermediates/aapt_proguard_file directory of your project and there are placed rules which AAPT2(Android Asset Package tool 2) generates based on references to classes in your app’s manifest, layouts and other app resources. For example, AAPT2 includes a keep rule for each Activity that you register in your app’s manifest as an entry point.
  4. If you include some AAR library as a compile time dependency, which is published with its own ProGuard rules file, R8 automatically applies its rules when compiling your project. From the one side this can be useful because you will have keep rules, that are required for the library to work properly. But from the other side certain rules that an AAR library dependency includes cannot be removed and might impact the compilation of other parts of your app.

To output a full report of all the rules that R8 applies when building your project, include the following in your module’s proguard-rules.pro file:

In /build/outputs/mapping/ directory you can find some useful files generated by R8 while building your APK:

  1. mapping.txt — this file provides translation between the original and obfuscated classes, methods and field names. This mapping file also contain information to map the line numbers back to the original source file line numbers;
  2. seeds.txt — this file has a list of the classes and class members which were not obfuscated;
  3. usage.txt — this file contains the code that was removed from the APK.

Custom ProGuard rules

The Android Gradle plugin and AAPT2 automatically generate keep rules that are required, such as app’s activities, views and services, but if R8 removes code that you actually need and your app start crashing, you can add custom keep rules in proguard-rules.pro file and prevent desired classes from being obfuscated.

List of ProGuard keywords, which you can use to make your own custom keep rules:

  • -keep — This rule doesn’t do shrinking and obfuscation for specified classes and also for its members;
  • -keepclasseswithmembers — it’s the same as -keep. The difference is that it only applies to classes which have all of the members in the class specification;
  • -keepnames — allows shrinking for classes and members, but not obfuscation. All unused code is going to be removed. But the code that is kept will keep its original names;
  • -keepclasseswithmembernames — this rule is the same as -keepnames. The only difference is that it only applies to classes which have all of the members in the class specification;
  • -keepclassmembers — This rule specifies class members to be preserved, if their classes aren’t removed in shrinking phrase;

Difference between this rule and -keep rule is that we’re not forcing preservation of all serializable classes, just preservation of the listed members of classes that are actually used.

  • -keepclassmembernames — This rule specifies class members to keep their names, if their classes aren’t removed in shrinking phrase; If class member isn’t used it will be removed also.

To enable you to later determine the line on which an exception occurred in a stack trace, proguard-rules.pro should contain following lines:

ProGuard rules for third-party libraries

When building the release version of the app, by default, R8 automatically performs the compile-time tasks. If you are using any third party library in your project you need to specify proGuard rules for that specific library. However, you can disable certain tasks or customize R8’s behavior through ProGuard rules in proguard-rules.pro file.

ProGuard rules for third party libraries you can find on library’s GitHub repos or documentation. Here are listed rules for some popular Android libraries.

ProGuard rules for GSON serialization

It’s good to mention that if you are using GSON for conversion from JSON to POJO objects, beside the standard ProGurd rules for GSON, you need to add all of your POJO classes to proguard-rules.pro file and prevent it from being obfuscated. This is required because conversion will fail if POJO field names are inferred from JSON response.

The following rule will prevent all classes from package model from being obfuscated:

Another way to solve this problem and if you want your models still being obfuscated use annotation @SerializedName(“name_of_json_field”). It will let GSON know the real name of the field.

Conclusion

If you are concerned about your app being reverse engineered, using a tool to obfuscate your code can help mitigate this threat. Obfuscating code modifies your source and machine code to be difficult for a human to understand if someone with malicious intentions de-compiles your app. Shrinking and optimization of the code removes the unused parts of the code and reduces the size of your app. Based of your needs for all of this purposes you can choose R8 or proGuard shrinker to do the job for you.

I hope you liked this article and you found it useful for your future work. Feel free to give me your feedback in comments, share or save post. :)

Happy Coding ! :)

Android developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store