- Troubleshooting ProGuard issues on Android
- Wojtek Kaliciński
- Translation from: The Gold Project
- This article is permalink: github.com/xitu/gold-m…
- Translator: dieyidezui
- Corresponding
Why use ProGuard
ProGuard is a tool to compress, optimize, and obfuscate code. Although there are many other tools available to developers, ProGuard is already packaged with the SDK as part of the Android Gradle build process.
There are many benefits to using ProGuard when building applications. Some developers are more concerned with obfuscating this feature, but for me the biggest use is to remove useless code from dex when packaging.
Topeka Sample app is an Android sample app.
The benefits of reducing packet size include increased user engagement and satisfaction, increased download speeds, and reduced installation time to connect users on end-devices, especially in emerging markets. Of course, there are times when you have to limit the size of your application, such as 4 MB for the Instant App, when ProGuard is essential.
If that’s not enough to convince you to use ProGuard, there are many more optimizations for removing useless code and confusing all names:
- On some versions of Android devices, the DEX code is compiled into machine code at install or run time. Both the original DEX and the optimized machine code remain on the device, so do the math: less code means less compile time and less storage.
- In addition to drastically reducing the code space, ProGuard can alsoHave shorter names for all identifiers (packages, classes, and members), such as
a.A
和a.a.B
. The process is confusion. Obfuscation reduces code in two ways: by making the string representing the name shorter; In cases where these methods or attributes have the same signature, the strings are more likely to be reused, ultimately reducing the number of string pools. - Using ProGuard is a prerequisite for enabling resource compression. Resource compression will remove resource files (such as image resources, which are usually the largest part of APK) that are not referenced by the code in your project.
- Removing code will help you avoid 64K dex method reference problems by packaging only the methods actually used in your code into APK. This can greatly reduce the need to use Multidex in your application, especially if you reference a lot of third-party libraries.
Should every Android app use code compression? I think so!
But before you jump with excitement, read on. When you turn ProGuard on, it can crash your application in some very subtle situations. While some errors occur while building your application and you can catch them in time, there are also errors that you can only catch at runtime, so make sure your application is thoroughly tested.
How to use ProGuard?
Enabling ProGuard in your project is as simple as adding the following lines of code to your main application module’s build.gradle file:
buildTypes {
/* you will normally want to enable ProGuard only foryour release builds, As it's an additional step that makes the build slower and can make debugging more difficult */ release {minifyEnabledtrueProguardFiles getDefaultProguardFile(' proguard-android.txt '), 'proguard-rules.pro'}}Copy the code
The configuration of ProGuard itself is done in a separate configuration file. In the code above, I have given the default configuration (¹) in the Android Gradle package, and I will add the other configuration in proguard-rules.pro.
You can find a user manual on the ProGuard website. Before you delve into these configurations, it’s a good idea to have a general understanding of how ProGuard works and why we specified some additional options.
You can also check out part of this Google I/O Session Shai Barack.
In simple terms, ProGuard takes the.class file in your project as input, then looks for all the call points in your code, calculates all the reachable call diagrams in your code, and removes the rest (that is, unreachable code and code that won’t be called).
You don’t need to look at the input/output sections when you read the ProGuard manual, because these Android Gradle packaging plug-ins will specify for you the input sources (code for you and third-party libraries) and the Android JAR libraries (The Android framework classes you use to build your application).
To properly configure ProGuard, the most important thing is to let it know which of your code should not be removed at runtime (and, of course, keep their names unchanged if obfuscating is turned on). When some classes and methods are accessed dynamically (such as using reflection), in some cases ProGuard does not make the right decisions about their “life or death” while building the call graph, resulting in the code being incorrectly removed. This also happens when you refer to your code only from XML resources (usually using low-level reflection).
During a typical Android build, AAPT (the tool that handles resources) generates an additional ProGuard rules file. It adds some special keep rules for Android applications, So the Activities, Services, BroadcastReceivers, and ContentProviders that you recorded in the Android manifest.xml stay put. This is why the MyActivity class was not removed or renamed in the GIF above.
AAPT also keeps all View classes (and their constructors) used in XML layout files and other classes, such as the transition classes referenced in transition animation resources. You can directly see the AAPT generated after build configuration files, the position is: < your_project > / < app_module > / build/intermediates/proguard rules / < variant > / aapt_rules. TXT.
A sample ProGuard profile generated by AAPT at build time
I’ll discuss more about the keep rule in a later section of this article, but until then we’d better learn what to do in the following cases:
When ProGuard interrupts your build
Before you can test to see if all the code works at runtime with ProGuard enabled, you need to build your application. Unfortunately, ProGuard may find that some referenced classes are missing and alert you, causing your build to fail.
The key to fixing this problem is to take a close look at the build-time output, understand the content of these warnings, and locate them. The usual approach is to fix your dependencies or add the -dontwarn rule to your ProGuard configuration.
One reason for these warnings is that your build path does not include JARs that require dependencies, such as using provided (compile-time only) dependencies. Sometimes, on Android, these code dependencies are not actually called at run time. Let’s look at a real world example.
A project relies on OkHttp 3.8.0 build time messages.
OkHttp library has added new annotations in 3.8.0 version of the class (javax.mail. The annotation. Nullable). But because they use the compile time dependence, so these annotations in the final build time wouldn’t be packaged in (even if the application explicitly depend on the com. Its code. The findbugs: jsr305), therefore ProGuard will complain about missing the class.
Since we know that these annotation classes will not be used at run time, we can safely ignore these warnings by adding the -dontwarn rule to the ProGuard configuration, such as in the OkHttp document:
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
Copy the code
You should have gone through a similar process, seeing these warnings in the output message, and then rebuilding until the build passes. It is important to understand why you are receiving these warnings and whether these classes are really missing at build time.
Now you might try to use the -ignoreWarnings option to simply ignore all warnings, but this is usually not a good idea. In some cases, ProGuard’s warnings do help you spot the culprits of flashbacks and other problems with your configuration.
You might want to take a look at Progard’s Notes (Messages with lower priority than warnings), which can help you spot some reflek-related issues. While it won’t interrupt your build, it may backslide at run time. This happens in the following scenario:
When ProGuard removes too many classes
In some cases, ProGuard is not aware that a class or method is being used, such as when the class is used only in reflection or is referred to only in XML. To prevent such code from being removed or confused, you should specify additional KEEP rules in the ProGuard configuration. It’s up to you, as the app developer, to find out which parts of the code are problematic and provide the necessary rules.
When a ClassNotFoundException or MethodNotFoundException occurs at runtime, it means that you must be missing some classes or methods, either because ProGuard removed them, or because the dependencies were misconfigured so that they could not be found. So the production build (when ProGuard is turned on) must focus on thorough testing and addressing these errors.
You have a number of options to configure your ProGuard:
- **keep ** – Keep all matched classes and methods
- ** KeepClassMembers ** – Keep specified members if and only if their class is retained for other reasons (referred to by other call points or kept by other rules)
- ** KeepClassesWithMembers ** – Keep a class and its members if and only if all members exist in a matching class
I recommend that you start with ProGuard’s Class Specification Syntax, which discusses all of the above keep rules and the -Dontwarn option discussed in the previous paragraph. There is also a different version of each of the three KEEP rules that supports only obfuscation (renaming) and no compression. You can see an overview in the form on the ProGuard website.
As an alternative to writing ProGuard rules, you can simply add @keep annotations to a class, method, or property that you don’t want to confuse and remove. Note that if you do this, you’ll need to add Android’s default ProGuard configuration to your build.
APK Analyzer and ProGuard
The Android Studio integrated APK Analyzer helps you see which classes have been removed by ProGuard and supports the generation of keep rules for them. If ProGuard is enabled when you build APK, you will output additional files in
/build/outputs/mapping/. These files contain information about removing code, confusing mappings.
Load the ProGuard mapping file into APK Analyzer to see more information in the DEX view
When you Load the mapping file to the APK Analyzer (click “Load Proguard Mappings… “Button), you can see some additional features in the DEX view tree:
- All names are pre-mix-up (that is, you can see the original name)
- Packages, classes, methods, and properties held by ProGuard configuration rules are shown in bold
- You can turn on the “Show Removed Nodes” option to see anything that has been removed by ProGuard (fonts will have deletion lines). Right-clicking a node in the tree allows you to generate a keep rule that you can paste into your profile.
When ProGuard removes too few classes
All applications can use Android’s built-in ProGuard security default rules, such as keeping the View’s getter and setter methods as they are often accessed by reflection, and other common methods and classes are not removed. This can prevent your application from crashing in many cases, but these configurations are not 100% appropriate for your application. You can remove the default ProGuard file and use your own.
If you want ProGuard to remove all unused code, you should avoid writing keep rules too broadly, such as including wildcards to match entire packages, and instead use class-related matching rules or the @keep annotation mentioned above.
Use the -whyareyoukeeping < class-Specification > option to see why these classes were not removed.
If you’re really not sure why ProGuard doesn’t remove the code you expect it to, you can add the -whyareyoukeeping option to the ProGuard profile and rebuild your application. In the build output, you’ll see what call chain determines that ProGuard keeps this code.
Trace in APK Analyzer what keeps these classes and methods in DEX
The other approach is less precise, but requires no rebuilding and no extra work in any application. That is, open the DEX file in APK Analyzer, and right click on the class or method that you are interested in. Select “Find Usages” and you’ll see a reference chain that may guide you to what part of the code uses the specified class or method to prevent it from being removed.
ProGuard and the stack after confusion
As I mentioned earlier, ProGuard outputs mapping and log files while processing class files during the build process. When you need to keep build artifacts, you should keep these files with the APK. These mapping files cannot be used by other builds, but are only guaranteed to be correct when used in conjunction with the APK generated with them. With these mappings, you can effectively debug crashes that occur on user devices. Otherwise it’s hard to pinpoint the problem because the names are all mixed up.
Upload the APK’s corresponding ProGuard mapping file to the Google Play console to get the stack information before the obfuscations.
When you release the obfuscated production APK on the Google Play Console, remember to upload the corresponding mapping file for each version. That way, when you look at the ANRs & Crashes page, the reported stack will be the actual class name, method name, and line number, not the shortened, confused ones.
About ProGuard and third-party libraries
Just as it is your responsibility to provide keep rules for your own code, it is the responsibility of the authors of third-party libraries to provide you with the necessary obfudged rule configurations to avoid build failures or application crashes caused by Proguard being turned on.
Some projects simply mention the necessary obfudging rules in their documents or on the README, so you need to copy and paste these rules into your main ProGuard configuration file. Better yet, third-party library maintainers who publish libraries as AAR can specify that the rules are wrapped in the AAR and automatically exposed to the build system at application build time by adding the following lines to the library module’s build.gradle file:
release { //or your own build typeConsumerProguardFiles' consumer - proguard. TXT '}Copy the code
The rules that you write in the consumer-proguard.txt file will be attached to the application master ProGuard configuration and used at application build time.
For more information on code and resource compression, please refer to our documentation page
Starting ProGuard may be difficult at first, but I personally think the costs are worth it. With a little time, you’ll have a lightweight, optimized application. In addition, taking the time to configure your application now means you’ll be ready when the experimental ProGuard replacement R8 is ready. Because R8 also works with the existing ProGuard rules file.
In addition to making your code smaller, ProGuard and R8 can choose to optimize your code to make it run faster, but that’s a topic for another article…
Creates proguard – android. TXT file before is in the SDK tools directory (SDK/tools/proguard/proguard – android. TXT). However, with the new SDK Tools and Android Gradle plugin version 2.2.0+, you can unzip the Android plugin jar at build time. After building your project, you can be in the < your_project > / build/intermediates/proguard files/directory to find the configuration file.
Thanks to Daniel Galpin.
Diggings translation project is a community for translating quality Internet technical articles from diggings English sharing articles. The content covers Android, iOS, React, front end, back end, product, design and other fields. If you want to see more high-quality translations, please continue to pay attention to The Jingjin Translation Project, official weibo, zhihu column.