Author: Liu Tianyu (Modest Wind)
Engineering corruption is a very difficult problem in app iteration. It involves extensive and detailed details and has a relatively “hidden” and indirect impact on R&D efficiency & experience, engineering & product quality, stability, package size and performance. Generally not cause unsustainable obstacles, jumped out but often lead to “pain”, a bit like cavities or wisdom teeth, at some point not not pull, but the difference is that the engineering of corruption by “pulling” once to effect a radical cure very hard, after any “pulling”, need effective sustainable management plan, to form a normalized anticorrosive system.
From the perspective of engineering corruption, it refers to the corruption of code engineering, engineering structure itself and various “elements” (manifest, code, resources, SO and configuration) that comprise APP. In recent years, youku architecture team has been continuously thinking, practicing and governing, and has accumulated some technologies, tools and solutions. Now, one by one, classified and summarized, supplemented by the explanation of relevant field knowledge, sorted into a series of technical articles titled “Firing a gun at engineering corruption”, to share with you. We hope that more students will join us in the long war against engineering corruption.
This article, the first in a series, will focus on the niche area of Java code, ProGuard. On engineering corruption, direct firing!
In the field of Android (Java) development, “Code ProGuard” is commonly mentioned. It refers to the use of ProGuard tools to cut, optimize and obturate Java code, so as to achieve tree-shaking, code logic optimization, symbol (class, variable, method) obturation. The proGuard process has a significant impact on apK build time, product controllability (runtime stability), package size, and performance.
A lot of times developers use the term “obfuscation” to refer to the entire Proguard process, which is inaccurate, but in context, as long as it doesn’t create ambiguity, it doesn’t hurt. It is worth noting that Google has replaced Proguard with R8 in the Android Gradle Plugin in recent years. However, the term “code Proguard” has become a slang term, and in this article, unless otherwise specified, “code ProGuard” refers to the process, not the ProGuard tool itself.
Basic knowledge of
This chapter first briefly introduces some basic knowledge, so that you can have a clear understanding of the “framework” of ProGuard.
Function is introduced
Proguard’s three core functions are as follows:
- Shrink. Detect and remove useless classes, variables, methods, and attributes through a holistic static analysis of all code reference relationships. It plays an important role in reducing the final APK.
- Optimize. This is the most complex part of the Proguard process. Through a deep analysis of the code execution logic, we can remove unnecessary code branches, method parameters, local variables, inline methods/classes, and even optimize instruction sets, which contain dozens of optimization items in total. This reduces code size footprint and, most importantly, reduces runtime method execution time.
- Obfuscate. Shortening class, variable and method names also plays an important role in reducing apK by reducing code size. At the same time, it is also a primary technical solution to increase the difficulty of apK anti-cracking.
The above three processes, shrink and optimize alternately, can loop multiple times depending on configuration (R8 is not configurable). A typical Proguard process is as follows:
Proguard process
App classes include all Java code in the Application project, sub Project, external dependencies AAR/JAR, Local JAR, and Flat Dir AAR. Library classes include Android Framework jars, Legacy jars, and other code that is only needed at compile time, provided by the system at runtime, and not packaged into APK.
Configuration items
Proguard provides powerful configuration items to customize the entire process. In this case, it is divided into global configurations and KEEP configurations. Note that R8 removes support for most global configurations in order to maintain consistent controllability of the processing and better processing results.
Global configuration
Global configuration refers to some configuration items that affect the overall processing process. Generally, they are classified into the following types:
1. Tailoring configuration
- – dontshrink. When specified, the clipping function is disabled.
- – whyareyoukeeping. Specify why the target class, variable, or method is “kept” and not clipped in APK. Note that the results given by R8 and Proguard do not have the same meaning. To get a sense of the contrast:
# example: type of TestProguardMethodOnly is keep rules ", "keep live directly, a method of TestProguardMethodOnly, calls the TestProguardFieldAndMethod method in a class. Explaining Why classes and class members are being kept if multiple keep rules/references explain why classes and class members are being kept. com.example.myapplication.proguard.TestProguardMethodOnly is kept by a directive in the configuration. com.example.myapplication.proguard.TestProguardFieldAndMethod is invoked by com.example.myapplication.proguard.TestProguardMethodOnly: Void methodAnnotation() (13:15) is kept by a directive in the configuration. # 1. "is kept by a directive in the configuration.", TestProguardMethodOnly is "kept" directly by the keep rule # 2. "is invoked by XXXX", TestProguardFieldAndMethod was TestProguardMethodOnly calls, lead to "keep up"; "Is kept by a directive in the configuration.", TestProguardMethodOnly is kept by a keep rule. That is, if the class is called by another reserved class, but no keep rule directly corresponds to this class, then the result given here, "Is" Nothing is keeping XXX. Com. Example myapplication. Proguard. TestProguardMethodOnly | - is referenced in the keep rule: | /Users/flyeek/workspace/code-lab/android/MyApplication/app/proguard-rules.pro:55:1 Nothing is keeping Com. Example. Myapplication. Proguard. TestProguardFieldAndMethod # interpretation results: # 1 "is referenced in the keep rule: XXX ", TestProguardMethodOnly is "kept" by this particular rule. However, if multiple rules "keep" the class, only one keep rule will be displayed here. # 2. "Nothing is keeping XXXX" TestProguardFieldAndMethod are not keep the rules ", "keep live directlyCopy the code
2. Optimize the configuration
- – dontoptimize. After the parameter is specified, the optimization function is disabled.
- – optimizationpasses. Optimization times. Theoretically, the more optimization times, the better the effect. Once there is no effect after one optimization, the next round of optimization will be stopped.
- – optimizations. Configure specific optimization items. For details, see the Proguard documentation. The following is a handy proGuard processing log to feel the optimization items:
Optimize item presentation
- The other. Including -assumenosideeffects, -allowAccessmodification, etc., the specific can refer to the document, no more details;
3. Confuse the configuration
- – dontobfuscate. When specified, the obfuscation function is disabled.
- The other. Including applymapping, obfuscationdictionary, useuniqueclassmembernames, dontusemixedcaseclassnames several configuration items, such as confusion for fine control process, For details, please refer to the documentation.
Keep the configuration
The KEEP configuration, as opposed to the global configuration, is the most familiar and commonly used to specify classes, variables, and methods that need to be retained. Classes that are directly hit by the Keep rule and thus remain are called seeds.
Here’s a question to consider: If apK was built without any keep rules, would the code have been clipped altogether? The answer is yes, ultimately there won’t be any code in APK. Some students may say, “I used Android Studio to create an APP project, and enabled Proguard but did not configure any keep rules. Why did apK eventually contain some code?” This is because the Android Gradle Plugin automatically generates some obfuscation rules during the process of building APK. The origin of all keep rules will be discussed in the following chapters.
Okay, let’s go back to the keep configuration. The rules supported by the KEEP configuration are quite complex and can be grouped here into the following categories:
1. Directly preserve classes, methods and variables;
- – keep. It is disallowed to shrink or obfuscate classes, methods, and variables to be preserved.
- – keepnames. Equivalent to -keep, allowshrinking. Preserve classes, methods, and variables, allow shrink, and disallow obfuscate if it is eventually preserved (other keep rules, or code calls).
2. If the class is retained (not clipped), retain the specified variables and methods;
- – keepclassmembers. Shrink is not allowed. Obfuscate is not allowed.
- – keepclassmembernames. Equivalent to -keepClassMembers, allowshrinking. Shrink allows for preserved variables and methods, and obfuscate is disallowed if it is eventually preserved.
3. If methods/variables meet the specified conditions, retain the corresponding classes, variables and methods;
- – keepclasseswithmembers. It is disallowed to shrink or obfuscate classes, methods, and variables to be preserved.
- Keepclasseswithmembernames. Equivalent to -keepClasseswithmembers, allowshrinking. Shrink is allowed for reserved classes, methods, and variables, and obfuscate is disallowed if it is eventually preserved.
The complete keep rule format is as follows, to feel the complexity:
-keepXXX [,modifier,...] class_specification # support modifiers: includedescriptorclasses includecode allowshrinking allowoptimization allowobfuscation # class_specification format: [@annotationtype] [[!]public|final|abstract|@ ...] [!] interface|class|enum classname [extends|implements [@annotationtype] classname] [{ [@annotationtype] [[! ]public|private|protected|static|volatile|transient ...] <fields> | (fieldtype fieldname [= values]); [@annotationtype] [[! ]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> | <init>(argumenttype,...) | Classname (argumenttype,...) | (returntype methodname (argumenttype,...)/return values);}] # in addition, different location support wildcards, of different level is not detailed.Copy the code
In practical work, the very complex keep rule is generally not used, so the complete usage need not deliberately learn, you can look up the document to understand. Let’s conclude this section with an interesting example.
= = = = = = = = = = = = = = = = = = = = = sample = = = = = = = = = = = = = = = = = = = = = # sample classes: package com. Example. Myapplication. Proguard; public class TestProguardFieldOnly { public static String fieldA; public int fieldB; } package com.example.myapplication.proguard; public class TestProguardMethodOnly { public static void methodA() { Log.d("TestProguardClass", "void methodA"); } } package com.example.myapplication.proguard; public class TestProguardFieldAndMethod { public int fieldB; public static void methodA() { Log.d("TestProguardClass", "void methodA"); }} # keep rules: - keepclasseswithmembers class com. Example. Myapplication. Proguard. {* * *; } # question: Which sample classes will be "kept" as a result of this keep rule? # answer: TestProguardFieldOnly and TestProguardFieldAndMethodCopy the code
Supporting documents
The auxiliary files mentioned here refer to the files generated by Progaurd to understand the results of the process and to help troubleshoot crop and obfuscation related problems (necessary).
Supporting documents
Set of configuration items
A collection of configuration items, which summarizes all configuration information and expands certain configurations. Because configuration items can be defined in multiple files, in multiple projects (all sources will be covered later), the collection of configuration items is convenient for viewing in this central.
Turn on this output with the configuration item -printConfiguration
, For example – printconfiguration build/outputs/proguard. CFG generates ${root} application engineering/build/outputs/proguard. CFG file, sample content is as follows:
Keep result (seeds.txt)
The keep result is a summary of classes, variables, and methods that the KEEP rule “preserves” directly. Note that classes, variables, and methods that are called by other reserved methods and cause indirect “reservations” are not in this result file.
Open this output with the configuration item -printseeds
, For example – printseeds build/outputs/mapping/seeds. TXT will generate ${root} application engineering/build/outputs/mapping/seeds. TXT file, sample content is as follows:
com.example.libraryaar1.proguard.TestProguardConsumerKeep: void methodA()
com.example.myapplication.MainActivity
com.example.myapplication.MainActivity: MainActivity()
com.example.myapplication.MainActivity: void openContextMenu(android.view.View)
com.example.myapplication.R$array: int planets_array
com.example.myapplication.R$attr: int attr_enum
Copy the code
Clipping result (usage.txt)
The clipping result is a summary of classes, variables, and methods that have been clipped.
Turn on this output with the configuration item -printUsage
, For example – printusage build/outputs/mapping/usage. TXT will generate ${root} application engineering/build/outputs/mapping/usage. TXT file, sample content is as follows:
androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
public boolean hasWindowFeature(int)
public void setHandleNativeActionModesEnabled(boolean)
Copy the code
Note that if the class is completely clipped, only the fully qualified name of the class is listed; If the class is not clipped but the variables and methods in the class are clipped, the class name is listed first, and then the variables and methods that are clipped are listed.
Confounding results (mapping.txt)
Clipping result is a summary of obfuscated classes, variables, and methods.
Turn on this output with the configuration item -printMapping
, For example – printmapping build/outputs/mapping/mapping. TXT will generate ${root} application engineering/build/outputs/mapping/mapping. TXT file, sample content is as follows:
===================== Proguard example: List all classes that are retained, And confusion results = = = = = = = = = = = = = = = = = = = = = com. Example.. Myapplication myapplication - > com. Example.. Myapplication myapplication: void <init>() -> <init> com.example.myapplication.proguard.TestProguardAndroidKeep -> com.example.myapplication.proguard.TestProguardAndroidKeep: int filedA -> filedA void <init>() -> <init> void methodA() -> methodA void methodAnnotation() -> methodAnnotation com.example.myapplication.proguard.TestProguardAnnotation -> com.example.myapplication.proguard.TestProguardAnnotation: com.example.myapplication.proguard.TestProguardFieldAndMethod -> com.example.myapplication.proguard.a: void methodA() -> a com.example.myapplication.proguard.TestProguardInterface -> com.example.myapplication.proguard.TestProguardInterface: void methodA() -> methodA com.example.myapplication.proguard.TestProguardMethodOnly -> com.example.myapplication.proguard.TestProguardMethodOnly: Void < init > () - > < init > void methodAnnotation () - > methodAnnotation = = = = = = = = = = = = = = = = = = = = = the R8 example: Only list is retained, and be confused classes, variables, methods of = = = = = = = = = = = = = = = = = = = = = # compiler: the R8 # compiler_version: 1.4.94 # min_api: 21 com.example.libraryaar1.LibraryAarClassOne -> a.a.a.a: void test() -> a com.example.libraryaar1.R$layout -> a.a.a.b: com.example.libraryaar1.R$styleable -> a.a.a.c: com.example.myapplication.proguard.TestProguardFieldAndMethod -> a.a.b.a.a: void methodA() -> aCopy the code
There are some differences between the output content of Proguard and R8, as well as the format. In the actual interpretation, you need to be careful.
The engineering application
With a general overview of proGuard basics, let’s take a look at some of the things you need to know in order to use ProGuard in an actual project. This section does not cover the basics, which can be easily found in official documents and various articles.
Tool selection
First, look at what tools are available. For Android development, there are two tools to choose from: Proguard and R8. The latter is Google’s official self-developed alternative to Proguard, which is better than Proguard in terms of cutting and optimizing processing time and processing effect. Here are some comparisons:
Although R8 does not provide global process control options, it does provide two modes:
- Normal mode. The optimize strategy is compatible with Proguard as much as possible, allowing apps to switch smoothly from Proguard to R8.
- Complete mode. In terms of optimization strategy, a more aggressive approach is adopted, so additional KEEP rules may be required to ensure code availability compared to Proguard. Open the way for the gradle. The properties file, add configuration: android. EnableR8. FullMode = true.
In terms of usability, R8 has reached a relatively mature state. It is suggested that we still use the APP of ProGuard and put the plan of switching R8 on the agenda as soon as possible. However, it should be noted that even in normal mode, the optimization strategy of R8 is still different from proGAurd to some extent. Therefore, comprehensive regression verification is required to provide quality assurance.
Custom Configuration
We have talked a lot about configuration items. How to add custom configuration rules in a specific project? I think most of you are going to think, well, this problem couldn’t be simpler, so let’s switch to another problem, where do the configurations that are ultimately involved in the process come from?
AAPT generates obfuscation rules, let’s look at a few examples to help you understand which keep rules have been added automatically, no manual handling:
# Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/app/build/intermediates/merged_manifests/fullRelease/AndroidManif est.xml:28 -keep class com.example.myapplication.MainActivity { <init>(); } # Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/app/build/intermediates/merged_manifests/fullRelease/AndroidManif est.xml:21 -keep class com.example.myapplication.MyApplication { <init>(); } # Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/library-aar-1/build/intermediates/packaged_res/release/layout/lay out_use_declare_styleable1.xml:7 -keep class com.example.libraryaar1.CustomImageView { <init>(...) ; } # Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/app/src/main/res/layout/activity_main.xml:9 -keepclassmembers class * { *** onMainTextViewClicked(android.view.View); }Copy the code
As you can see, the function name corresponding to the onClick property value in layout cannot be confused and generates a rule that is prone to excessive keep, so this is not recommended in real code.
Special care needs to be taken with configurations carried in sub-projects/external modules, which can have unexpected results if not handled carefully.
Governance practices
The first two chapters explain the basic knowledge and engineering application of ProGuard, and it is believed that everyone has formed a preliminary overall cognition of ProGuard. As configuration items come from a wide range of sources, especially the consumerProguard mechanism, dependent external modules may carry “problem” configuration items, which makes it difficult to control the overall configuration items. In addition, the Keep configuration is separated from the object code and is easily preserved after code is deleted. In engineering practice, with the continuous iteration of APP, the following two types of problems will be encountered:
- The global configuration was modified unexpectedly. Procedure If the code is confused, clipped, optimized times and optimized types are modified, the code will change greatly, affecting stability, package size and performance;
- A. keep b. keep C. keep D. keep The number of KEEP rules is non-linear proportional to the proGuard time spent during construction (removing useless/redundant KEEP rules can improve construction speed). An overly broad KEEP rule can cause package sizes to increase and code to fail to be optimized, affecting runtime performance.
“If a worker wants to do a good job, he must sharpen his tools first”. Before the actual treatment, the development of detection tools was carried out respectively. Based on the test results provided by the tool, the governance work is carried out separately. (The tools involved in this article are all part of the “Onepiece Detection and Analysis Suite” developed by Youku)
Global configuration
Global configuration detection capability (tool) provides proGuard global configuration detection capability. Based on the whitelist mechanism, it can detect the inconsistency between the value of the target configuration item and the whitelist in a timely manner. It also provides the option to terminate the build process and prompt when unexpected changes occur to the global configuration.
If the global configuration is inconsistent with the whitelist, the generated detection result file lists the inconsistent configuration items. The following is an example:
* useUniqueClassMemberNames
|-- [whitelist] true
|-- [current] false
* keepAttributes
|-- [whitelist] [Deprecated, Signature, Exceptions, EnclosingMethod, InnerClasses, SourceFile, *Annotation*, LineNumberTable]
|-- [current] [Deprecated, Signature, Exceptions, EnclosingMethod, InnerClasses, SourceFile, AnnotationDefault, *Annotation*, LineNumberTable, RuntimeVisible*Annotations]
Copy the code
With this detection capability, critical global configurations are protected against unexpected changes (potholes, of course, are stepped on more than once…). .
Keep the configuration
Keep configuration governance is much more difficult. In terms of impact on the final APK, the KEEP configuration can be divided into the following four categories:
- Useless rules. It has absolutely no effect on the final processing result. In other words, if a keep rule does not match any class, it is a useless rule;
- Redundancy rules. The keep effect of one rule can be completely covered by one or more existing rules. This leads to unnecessary configuration parsing and time-consuming processing (every KEEP rule is matched against all classes);
- Overrule. Go beyond the necessary keep scope to preserve unnecessary classes, variables, and methods. This also includes cases where only keepnames are required, but keep is kept;
- Precise rules. The necessary rules to follow the principle of minimum reservation. There is no need to deal with it, but it should be noted that the self-developed business code in the APP should try to use the @Keep annotation provided in Support or androidX to keep the keep rules together with the code.
The first three types of rules above all belong to governance objectives. Now we compare the difficulty of these three types of rules from the three dimensions of analysis, processing and verification.
Keep rule governance difficulty comparison
1, analysis,
- Useless. By matching each KEEP rule to each class, you can determine if it has “impact” on that class. The difficulty of matching is mainly due to the complexity of the KEEP rule and the consistency with the matching results of ProGuard.
- Redundancy. If it is a rule, the effect is completely “contained” by other rules. This can be done by first calculating the influence of each KEEP rule on each class, and finally finding out the same “reserved” scope or “contained” relationship, which can be theoretically realized. However, when a rule is “contained” by more than one rule, the detection complexity becomes very high.
- Too much. This is almost impossible to detect precisely, because which classes, variables, and methods should be kept depends on how they are used at run time. If excessive rules can be detected, then all keep rules theoretically need not be added manually;
2, processing,
- Useless. Delete directly;
- Redundancy. Delete one or more rules, or combine several rules;
- Too much. Add qualifiers, rewrite rules, etc. Need to have a clear understanding of the expected results and a good command of the KEEP rules;
3, validation,
- Useless. It has no effect on the final cutting and confusion results. The “clipping result” and “confusion result” in the auxiliary file can be verified. In order to further confirm the impact, apK itself can also be compared and verified.
- Redundancy. Like useless rules, they have no impact on processing results and the verification methods are the same.
- Too much. To the final tailoring, optimization, confusion results, all have an impact. It needs to be verified by functional regression.
In the tool development, an auxiliary positioning function and three detection capabilities are realized:
1. The [auxiliary] module contains a list of KEEP rules. Each module contains keep rules, which makes it easy to check the source of each KEEP rule.
Project: app: 1.0 | -- - keepclasseswithmembers class com. Example. Myapplication. Proguard. {* * *; } |-- -keepclassmembers class com.example.myapplication.proguard.** { * ; } |-- -keep class com.example.libraryaar1.CustomImageView { <init> ( ... ) ; } |-- -keep class com.example.myapplication.proguard.** |-- -keepclasseswithmembers class * { @android.support.annotation.Keep <init> ( ... ) ; } project: library - the aar - 1 to 1. 0 | -- - keep interface * {< the methods >; }Copy the code
2. [Detection] Keep rule matching class detection. Each KEEP rule, which classes are hit, and which modules those classes belong to.
* [1] -keep class com.youku.android.widget.TextSetView { <init> ( ... ) ; } / / this is keep the rules, the Numbers [x], says the number of rules keep hit module | -- [1] com. Youku. The android: moduleOne: 1.21.407.6 / / it is keep a module, the number of the [x], Said the number of classes to be hit the module | | -- com. Youku. Android. Widget. TextSetView / / this is module, * [2] -keep public class com.youku.android.vo.** {*; } | - [32] com. Youku. Android: ModuleTwo: 1.2.1.55 | | -- com. Youku. Android. Vo. MessageSwitchState $XXX | | - com.youku.android.vo.MessageCenterNewItem$xxxx ...... | - [14] com. Youku. Android: ModuleThree: 1.0.6.47 | | -- com. Youku. Android. Vo. MCEntity | | -- com. Youku. Android. Vo. NUMessage | |-- com.youku.android.vo.RPBean$xxxx ......Copy the code
3, [detection] class is detected by keep rule matching. Each class (and its module) is hit by the keep rule. This test focuses on classes that are directly “affected” by the keep rules as opposed to -whyAreyoukeeping.
* com. Youku. Arch: ModuleOne: 2.8.15 / / this module is maven coordinates | -- com. Youku. Arch. SMBridge / / this is the class name, The following to keep hitting these rules list | | -- - keepclasseswithmembers, includedescriptorclasses class * {native < the methods >; } | |-- -keepclasseswithmembernames class * { native <methods> ; } | |-- -keepclasseswithmembers class * { native <methods> ; } | |-- -keepclassmembers class * { native <methods> ; } |-- com.youku.arch.CFixer | |-- -keepclasseswithmembers , includedescriptorclasses class * { native <methods> ; } | |-- -keepclasseswithmembernames class * { native <methods> ; } | |-- -keepclasseswithmembers class * { native <methods> ; } | |-- -keepclassmembers class * { native <methods> ; }Copy the code
4, [detection] useless keep rule detection. Which keep rules don’t hit any classes.
* -keep class com.youku.android.NoScrollViewPager { <init> ( ... ) ; } * -keep class com.youku.android.view.LFPlayerView { <init> ( ... ) ; } * -keep class com.youku.android.view.LFViewContainer { <init> ( ... ) ; } * -keep class com.youku.android.view.PLayout { <init> ( ... ) ; } * [ignored] -keep class com.youku.android.view.HAListView { <init> ( ... ) ; } * -keep class com.youku.android.CMLinearLayout { <init> ( ... ) ; } * [ignored] -keepclassmembers class * { *** onViewClick ( android.view.View ) ; } // When a keep rule is in the ignoreKeeps configuration, the tag [ignored] is addedCopy the code
In addition, comparative analysis tools of “clipping results” and “obfuscating results” are provided to verify the cleaning results of useless/redundant KEEP rules.
= = = = = = = = = = = = = = = = = = = = = cutting result contrast = = = = = = = = = = = = = = = = = = = = = * - [add] android. Support. The annotation. VisibleForTestingNew * - [delete] com.youku.arch.nami.tasks.refscan.RefEdge *- [delete] com.example.myapplication.R$style *- [modify] com.youku.arch.nami.utils.elf.Flags | *- [add] private void testNew() | *- [delete] public static final int EF_SH4AL_DSP | * - [delete] public static final ints EF_SH_DSP = = = = = = = = = = = = = = = = = = = = = confusion results contrast = = = = = = = = = = = = = = = = = = = = = * - [add] com.cmic.sso.sdk.d.q | *- [add] a(com.cmic.sso.sdk.d.q$a) -> a | *- [add] <clinit>() -> <clinit> *- [delete] com.youku.graphbiz.GraphSearchContentViewDelegate | *- [delete] mSearchUrl -> h | *- [delete] <init>() -> <init> *- [modify] com.youku.alixplayermanager.RemoveIdRecorderListener ([new]com.youku.a.f : [old]com.youku.b.f) *- [modify] com.youku.saosao.activity.CaptureActivity ([new/old]com.youku.saosao.activity.CaptureActivity) | *- [modify] hasActionBar() ([new]f : [old]h) | *- [modify] showPermissionDenied() ([new]h : [old]f) *- [modify] com.youku.arch.solid.Solid ([new/old]com.youku.arch.solid.e) | *- [add] downloadSo(java.util.Collection,boolean) -> a | *- [delete] buildZipDownloadItem(boolean,com.youku.arch.solid.ZipDownloadItem) -> aCopy the code
According to the analysis tool, 758 (20%) of the 3812 KEEP rules failed to hit any class and were found to be useless rules. 700 of them were cleaned up and compared with “cropped results” and “obfuscated results” to ensure no impact on the final APK. Most of the rest comes from rules generated automatically when AAPT compiles resources, but the classes referenced in the resources do not exist in APK, making the KEEP rule useless. To clean up these rules, remove references to nonexistent classes from resources and whitelist them for now.
Reference to a nonexistent class in # layout does not cause a build failure during APK compilation, but it still generates the corresponding KEEP rule. Once this layout is "loaded" at runtime, it raises an exception that the Java class cannot find. <? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.myapplication.NonExistView android:id="@+id/main_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" / > < / LinearLayout > # generated keep rule is: to keep the class com. Example. Myapplication. NonExistView {< init > (...). ; }Copy the code
For redundant rules and excessive rules, a small batch trial cleaning is initially carried out, which is of high complexity and difficult to control the risk. Therefore, batch cleaning is not carried out at first and then gradually cleared.
Keep rule distribution & cleaning results
Thus, progurad processing time in youku’s complete release package construction was reduced by 18%. Next, on the one hand, the application project implements centralized control (Youku disables the consumerProguard of external modules), isolates configuration files by team, and establishes the access mechanism of KEEP rules. On the other hand, the useless KEEP configuration is used as a bayonet and deployed during release iterations to enter the normalized governance phase.
Governance landscape
Finally, a panorama of proGuard corruption governance is presented:
Proguard governance panorama
What else can be done
Engineering decay of other subdivisions of the battlefield is still ongoing. As for ProGuard governance, further exploration will be made on “redundant KEEP rules” and “excessive Keep rules” in terms of tool detection capability. On the other hand, it is not an overnight task to clean up the stock keep rules, and there is a long way to go.
【 References 】
- Proguard official documentation: www.guardsquare.com/manual/conf…
- The R8 official documentation: developer.android.com/studio/buil…
- ConsumerProguardFiles official documentation: developer.android.com/studio/proj…
Pay attention to [Alibaba mobile technology] wechat public number, every week 3 mobile technology practice & dry goods to give you thinking!