2
头图

Author: Liu Tianyu (Qianfeng)

Engineering corruption is a very difficult problem in the process of app iteration. It involves extensive and fine-grained specific details. It has relatively "hidden" and indirect effects on R&D efficiency & experience, engineering & product quality, stability, package size, and performance. Impact. Generally, it will not cause unbearable obstacles, but it often pops out and causes "labor pains", which are a bit like tooth decay or wisdom teeth. It cannot be removed after reaching a certain level. After any "pulling out", an effective sustainable governance plan is required to form a normalized anti-corrosion system.

From the perspective of project corruption and disassembly, it is the corruption of the project structure itself and various "elements" (manifest, code, resources, so, configuration) in the code project that composes the app. Youku Architecture Team In recent years, they have continued to think, practice and manage, and have accumulated some technologies, tools and solutions. It is now classified and summarized one by one, supplemented by the explanation of relevant field knowledge, and organized into a series of technical articles on "Burning the Corruption of Engineering" to share with you. I hope that more students will join in this protracted battle against engineering corruption.

This article is the first in a series of articles that will focus on java code proguard, this subdivision. For engineering corruption, shoot directly!

In the field of Android (java) development, "code proguard" is generally mentioned, which refers to the use of Proguard tools to cut, optimize, and obfuscate java code, so as to achieve useless code removal (tree-shaking), code logic optimization, symbols (classes) , variable, method) confusion. The proguard processing process has an important impact on the time-consuming of apk construction, product controllability (runtime stability), package size, and performance.

Many times developers will use "obfuscation" to refer to the entire Proguard process. Although it is inaccurate, it is not harmful if it is understood in context, as long as there is no ambiguity. It is worth noting that Google has replaced the Proguard tool with the self-developed R8 tool in the Android Gradle Plugin in recent years to complete the above three functions. However, the term "code proguard" has become an idiom. Unless otherwise specified in this article, "code proguard" refers to the processing process, not the Proguard tool itself.

Basic knowledge

This chapter briefly introduces some basic knowledge, so that everyone can have a clear understanding of the "framework" of proguard.

Features

The three core functions of Proguard are as follows:

  • Shrink. Through the overall static analysis of all code reference relationships, useless classes, variables, methods, and properties are detected and removed. It plays an important role in reducing the final apk;
  • optimize. This is the most complicated part of the entire Proguard process. Through in-depth analysis of code execution logic, useless code branches, method parameters, local variables are removed, methods/classes are inlined, and even optimization instruction sets are included, including dozens of optimization items in total. On the one hand, it can reduce the size of the code, on the other hand, and most importantly, it can reduce the execution time of the runtime method;
  • obfuscate. By shortening the names of classes, variables, and methods, the code size is reduced, which also plays an important role in reducing the final apk. At the same time, it is also a primary technical solution to increase the difficulty of anti-cracking of apk.

The above three processing processes, shrink and optimize, are alternately performed, and can be cycled multiple times according to the configuration (the number of cycles cannot be configured in R8). A typical Proguard process is as follows:


Proguard process

Among them, app classes include all java code in application project, sub project project, external dependency aar/jar, local jar, flat dir aar. Library classes include android framework jars, legacy jars and other codes that are only required at compile time. They are provided by the system at runtime and will not be packaged into apk.

configuration item

Proguard provides powerful configuration options to customize the entire process. Here, it is divided into two categories: global configuration and keep configuration. Note that in order to maintain the consistent controllability of the processing process and better processing results, R8 cancels support for most global configurations.

global configuration

Global configuration refers to some configuration items that affect the overall processing process. Generally, it can be divided into the following categories:

1. Crop configuration

  • -dontshrink. After specifying, close the crop function;
  • -whyareyoukeeping. Specify the target class, variable, and method, why are they "keep" and not cut out in the apk. Note that the results given by R8 and Proguard have different meanings. Let's take a look at the comparison:
# 示例:类TestProguardMethodOnly被keep规则直接“keep住”,TestProguardMethodOnly中的一个方法中,调用了TestProguardFieldAndMethod类中的方法。

# Proguard给出的结果,是最短路径,即如果多个keep规则/引用导致,只会给出最短路径的信息
Explaining 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是被keep规则直接“keep住”
# 2. “is invoked by xxxx",TestProguardFieldAndMethod是被TestProguardMethodOnly调用,导致被“keep住”;“is kept by a directive in the configuration.”,TestProguardMethodOnly被keep规则直接“keep住”


# R8给出的结果,是类被哪个keep规则直接命中,即如果类被其他保留下来的类调用,但是没有keep规则直接对应此类,那么此处给出的结果,是“Nothing is keeping xxx"
com.example.myapplication.proguard.TestProguardMethodOnly
|- is referenced in keep rule:
|  /Users/flyeek/workspace/code-lab/android/MyApplication/app/proguard-rules.pro:55:1
Nothing is keeping com.example.myapplication.proguard.TestProguardFieldAndMethod
# 结果解读: 
# 1. “is referenced in keep rule: xxx”,TestProguardMethodOnly是被具体的这一条规则直接“keep住”。不过,如果有多条规则均“keep住”了这个类,在此处只会显示一条keep规则。
# 2. “Nothing is keeping xxxx",TestProguardFieldAndMethod没有被keep规则直接“keep住”

2. Optimize configuration

  • -dontoptimize. After specifying, turn off the optimization function;
  • -optimizationpasses. The number of optimizations. In theory, the more optimizations, the better the effect. Once there is no effect after a certain optimization, the next round of optimization will be stopped;
  • -optimizations. To configure specific optimization items, refer to the Proguard documentation for details. The following is a log of the proguard processing process that I found at random. You can feel the optimization items:


Optimize item display

  • other. Including -assumenosideeffects, -allowaccessmodification, etc., please refer to the document for details, and will not be described in detail;

3. Confusion configuration

  • -dontobfuscate. After specifying, turn off the obfuscation function;
  • other. It includes several configuration items such as -applymapping, -obfuscationdictionary, -useuniqueclassmembernames, dontusemixedcaseclassnames, etc., which are used to finely control the obfuscation process. For details, please refer to the documentation.

keep configuration

Compared with the global configuration, the keep configuration is the most familiar and commonly used, and is used to specify the classes, variables, and methods that need to be retained. The classes that are directly hit by the keep rule and then remain are called seeds.

Here, we can think about a question: If there are no keep rules during the apk build process, will all the code be cut out? The answer is yes, there won't be any code in the final apk. Some students may say that I used Android Studio to create a new app project, turned on Proguard but did not configure any keep rules, why does the final apk contain some code? This is because the Android Gradle Plugin will automatically generate some obfuscation rules in the process of building apk. The source of all keep rules will be discussed in the following chapters.

Well, continue to go back to the keep configuration. The rules supported by the keep configuration are very complex and are divided into the following categories here:

1. Directly retain classes, methods, and variables;

  • -keep. Reserved classes, methods, variables, shrink (cropping), and obfuscate (obfuscation) are not allowed;
  • -keepnames. Equivalent to -keep, allowshrinking. Retain classes, methods, and variables, allow shrink, and if they are eventually retained (other keep rules, or code calls), then obfuscate is not allowed;

2. If the class is retained (not cut out), the specified variables and methods are retained;

  • -keepclassmembers. The reserved variables and methods are not allowed to shrink (crop) or obfuscate (confusion);
  • -keepclassmembernames. Equivalent to -keepclassmembers, allowshrinking. The reserved variables and methods are allowed to shrink, and if they are finally reserved, then obfuscate is not allowed;

3. If the method/variable meets the specified conditions, the corresponding class, variable, and method are retained;

  • -keepclasseswithmembers. Reserved classes, methods, variables, shrink (cropping), and obfuscate (obfuscation) are not allowed;
  • keepclasseswithmembernames. Equivalent to -keepclasseswithmembers, allowshrinking. Retained classes, methods, and variables are allowed to shrink, and if they are eventually retained, obfuscate is not allowed.

The complete keep rule format is as follows, 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 actual work, very complex keep rules are generally not used, so the complete usage does not need to be studied deliberately, and you can understand it by checking the documentation when you encounter it. End this section with an interesting example.

===================== 示例 =====================
# 示例类:
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规则:
-keepclasseswithmembers class com.example.myapplication.proguard.** {
    *;
}

# 问题:上述这条keep规则,会导致哪几个示例类被“保留”?
# 答案:TestProguardFieldOnly和TestProguardFieldAndMethod

Auxiliary documents

The auxiliary files to be mentioned here refer to some files generated by progaurd, which are used to understand the processing results, and are very helpful (necessary) for troubleshooting, tailoring, and confusion related problems.


Auxiliary documents

Configuration item collection

A collection of configuration items, which summarizes all configuration information and "expands" some configurations. Since configuration items can be defined in multiple files and projects (all sources will be discussed later), the collection of configuration items is convenient for us to view this in a centralized manner.

Open this output through configuration item -printconfiguration <filepath> . For example, -printconfiguration build/outputs/proguard.cfg will generate ${application project root directory}/build/outputs/proguard.cfg file. The example content is as follows:

keep results (seeds.txt)

The keep result is a summary of the classes, variables, and methods directly "retained" by the keep rules. Note that the classes, variables, and methods that are called by other reserved methods, resulting in indirect "reservation", are not in this result file.

Open this output through configuration item -printseeds <filepath> . For example, -printseeds build/outputs/mapping/seeds.txt will generate ${application project root directory}/build/outputs/mapping/seeds.txt file. The example 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

Crop result (usage.txt)

The clipping result is a summary of the clipped classes, variables, and methods.

Open this output through configuration item -printusage <filepath> . For example, -printusage build/outputs/mapping/usage.txt will generate ${application project root directory}/build/outputs/mapping/usage.txt file, the example 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)

Note that if the class is completely trimmed, only the fully qualified name of the class is listed; if the class is not trimmed, but the variables and methods in the class are trimmed, the class name will be listed here first, and then the trimmed ones will be listed. variables, methods.

Obfuscated results (mapping.txt)

The clipping result is a summary of the obfuscated classes, variables, and methods.

Open this output through configuration item -printmapping <filepath> . For example, -printmapping build/outputs/mapping/mapping.txt will generate ${application project root directory}/build/outputs/mapping/mapping.txt file. The example content is as follows:

===================== Proguard示例:列出被保留的所有类,以及混淆结果 =====================
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

===================== R8示例:仅列出被保留,且被混淆的类、变量、方法 =====================
# compiler: 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() -> a

There are some differences in the output content, and format, of Proguard and R8. In the actual interpretation, need to pay attention.

Engineering Applications

After having an overall "framework" awareness of the basic knowledge of proguard, let's take a look at some things that need to be understood in order to better use proguard in actual projects. This section will not describe the most basic usage, which can be easily found in the official documentation and various articles.

Tool selection

First, see what tools are available. For the field of Android development, there are two tools to choose from, Proguard and R8 (a long time ago, there was also an AGP - a code cutting tool built into the Android Gradle Plugin, which is completely outdated and no longer listed), of which the latter is officially self-developed by google The Proguard tool alternative is better than the Proguard tool in terms of time-consuming processing of cropping and optimization, as well as processing effect. Some comparisons between the two are as follows:

Although R8 does not provide global process control options, it provides two modes:

  • normal mode. The optimize strategy is as compatible with Proguard as possible to the greatest extent possible. Generally, apps can switch from Proguard to R8 normal mode relatively smoothly;
  • full mode. In terms of optimization strategy, a more aggressive solution is adopted, so compared to Proguard, additional keep rules may be required to ensure code availability. The opening method is to add the configuration in the gradle.properties file: android.enableR8.fullMode=true.

In terms of usability, R8 has reached a relatively mature state. It is recommended to still use the proguard app and put the R8 switching plan on the agenda as soon as possible. However, it should be noted that even in normal mode, the optimization strategy of R8 is different from that of progaurd. Therefore, comprehensive regression verification is required to provide quality assurance.

custom configuration

I have talked a lot about configuration items. How to add custom configuration rules in a specific project? Most of the students should think that this problem cannot be simpler, so let's change the problem and finally participate in the configuration of the processing process, where does it come from?

Let's look at a few examples of the obfuscation rules generated by AAPT, which will help you understand which keep rules have been added automatically without manual processing:

# Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/app/build/intermediates/merged_manifests/fullRelease/AndroidManifest.xml:28
-keep class com.example.myapplication.MainActivity { <init>(); }
# Referenced at /Users/flyeek/workspace/code-lab/android/MyApplication/app/build/intermediates/merged_manifests/fullRelease/AndroidManifest.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/layout_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); }

It can be seen that the function name corresponding to the onClick attribute value in the layout cannot be confused, and a rule that easily leads to excessive keep will be generated. Therefore, this method of use is not recommended in actual code.

Special attention should be paid to configurations carried in subprojects/external modules, which can lead to unexpected results if not handled carefully.

governance practice

In the first two chapters, the basic knowledge of proguard and its engineering application were explained. I believe that everyone has formed a preliminary overall understanding of proguard. Due to the wide source of configuration items, especially the existence of the consumerProguard mechanism, the dependent external modules may carry "problem" configuration items, which makes it difficult to control the configuration items as a whole. In addition, the keep configuration is separated from the target code. After the code is deleted, the keep configuration is very easy to be retained. In engineering practice, with the continuous iteration of the app, the following two types of problems will be encountered:

  • Global configuration, modified unexpectedly. Whether confusion, clipping, optimization times, optimization types, etc., once modified, will lead to major changes in the code, affecting stability, package size, and performance;
  • The keep configuration, which keeps increasing, gradually corrodes. The number of keep rules is nonlinearly proportional to the proguard time spent in the build process (removing useless/redundant keep rules can improve the build speed). Overly broad keep rules can lead to increased package size and code that cannot be optimized, which in turn affects runtime performance.

"If you want to do a good job, you must first sharpen your tools." Before the actual management, the development of detection tools was carried out. Based on the detection results provided by the tool, the governance work is carried out separately. (The tools involved in this article are all part of Youku's self-developed "onepiece detection and analysis suite")

Global configuration

The global configuration detection capability (tool) provides the global configuration detection capability of proguard, and based on the whitelist mechanism, it can detect the inconsistency between the value of the target configuration item and the whitelist in time. At the same time, provide options to terminate the build process and give a prompt when the global configuration changes unexpectedly.

When there is a global configuration inconsistent with the whitelist, the generated detection result file will list the inconsistent configuration items. The example content is as follows:

* 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]

Through this detection capability, the protection of key global configurations is achieved, thereby effectively avoiding unexpected changes (of course, the pits are stepped on, more than once...).

keep configuration

The management of keep configuration is much more difficult. In terms of the 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, then this rule is a useless rule;
  • Redundancy rules. The keep effect of a rule can be completely contained by one or more other existing rules. This will lead to unnecessary configuration parsing and increased processing time (every keep rule will be used to match all classes);
  • Excessive rules. Beyond the necessary keep scope, unnecessary classes, variables, and methods are reserved. Here, it also includes the case where only keepnames are needed, but keep is kept directly;
  • Precise rules. Follow the necessary rules of the principle of least reservation. There is no need to deal with it, but it should be noted that the self-developed business code in the app should use the @keep annotation provided in support or androidX as much as possible, so that the keep rules and the code are put together.

The first three types of rules above all belong to governance goals. Now, we will compare the difficulty of these three types of rules from the three dimensions of analysis, processing, and verification.


Comparison of the difficulty of keep rule governance

1. Analysis

  • useless. By matching each keep rule with each class, you can determine whether there is an "influence" on this class. The difficulty of this matching mainly comes from the complexity of the keep rule and the consistency with the matching result of proguard;
  • redundancy. If it is a rule, the effect is completely "contained" by other rules. In this way, you can first calculate the impact of each keep rule on each class, and finally find out that the "reserved" range is the same, or has a "contained" relationship. In theory, can be realised. But for a rule, when it is "included" by other rules, the detection complexity will become very high;
  • over. This is basically impossible to detect accurately, because which classes, variables, and methods should be retained, it is necessary to judge by "how to be used at runtime". If excessive rules can be detected, all keep rules theoretically do not need to be added manually;

2. Processing

  • useless. Just delete it directly;
  • redundancy. delete one or more of the rules, or combine several rules;
  • over. Add qualifiers, rewrite rules, etc. It is necessary to have a clear understanding of the expected effect and mastery of the keep rules;

3. Verify

  • useless. It has no effect on the final cropping and obfuscation results. It is enough to verify the "cutting result" and "confusion result" in the auxiliary file. In order to further confirm the impact, you can also compare and verify the apk itself;
  • redundancy. Like the useless rules, they have no effect on the processing results, and the verification methods are also the same;
  • over. It has an impact on the final cropping, optimization, and obfuscation results. It needs to be verified by means of functional regression.

In tool development, an auxiliary positioning function and three detection capabilities are implemented:

1. The [Auxiliary] module contains a list of keep rules. The keep rules contained in each module are convenient to view 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-aar-1:1.0
|-- -keep interface * { <methods> ; }

2. [Detection] Keep rule hit class detection. For each keep rule, which classes are hit, and which modules these classes belong to.

* [1] -keep class com.youku.android.widget.TextSetView { <init> ( ... ) ; }    // 这是keep规则,[x]中的数字,表示keep规则命中模块的数量
|-- [1] com.youku.android:moduleOne:1.21.407.6     // 这是keep命中模块,[x]中的数字,表示模块中被命中类的数量
|   |-- com.youku.android.widget.TextSetView    // 这是模块中,被命中的类

* [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
......

3. The [Detection] class is hit and detected by the keep rule. Each class (and the module it belongs to) is hit by which keep rules. Relative to -whyareyoukeeping, this checks which keep rules the focus class is directly "affected" by.

* com.youku.arch:ModuleOne:2.8.15   // 这个是模块maven坐标
|-- com.youku.arch.SMBridge    // 这个是类名称,以下为命中此类的keep规则列表
|   |-- -keepclasseswithmembers , includedescriptorclasses class * { native <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> ; }

4. [Detection] Useless keep rule detection. Which keep rules miss 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 ) ; }  // 当某条keep规则位于ignoreKeeps配置中时,会加上[ignored]标签

In addition, comparative analysis tools for "cutting results" and "confusion results" are provided to facilitate verification of the cleaning results of useless/redundant keep rules.

===================== 裁剪结果对比 =====================
*- [add] android.support.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 int EF_SH_DSP

===================== 混淆结果对比 =====================
*- [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) -> a

Youku host and guest, the governance baseline version, has a total of 3812 keep rules. Through the analysis tool, it is found that 758 (20%) of them do not hit any class, which are useless rules. 700 of them were cleaned up, and by comparing the "cropped result" and "confused result", it was ensured that it had no effect on the final apk. Most of the rest come from the rules automatically generated when AAPT compiles resources, but the classes referenced in the resources do not exist in the apk, which makes the keep rules useless. To clean up these rules, you need to delete the references to these non-existing classes in the resource and add them to the whitelist temporarily.

# layout中引用不存在的class,在apk编译过程中,并不会引发构建失败,但依然会生成相对应的keep规则。
# 这个layout一旦在运行时被“加载“,那么会引发Java类找不到的异常。

<?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>

# 生成的keep规则为:-keep class com.example.myapplication.NonExistView { <init> ( ... ) ; }

For redundant rules and excessive rules, a small batch of trial cleaning was initially carried out. The complexity is high, and the risk is difficult to control. Batch cleaning is not performed first, and then gradually cleaned up.


keep rule distribution & clean result

So far, the progurad processing time in the construction of Youku's complete release package has been reduced by 18%. Next, on the one hand, centralized control is implemented in the application project (Youku has disabled the consumerProguard of external modules), the configuration files are isolated by team, and the keep rule access mechanism is formulated; on the other hand, the useless keep configuration is used as a checkpoint item, It is deployed during the version iteration process and enters the normalized governance stage.

governance panorama

Finally, give a panorama of proguard corruption governance:


Proguard governance panorama

what else can be done

Other subdivision battlefields of Engineering Corruption are still in progress. For proguard governance, on the one hand, in terms of tool detection capabilities, we will conduct some explorations on "redundant keep rules" and "excessive keep rules". And the road is far, and I encourage you with all the lords.

【Reference Documents】

pay attention to [Alibaba Mobile Technology] WeChat public account, 3 mobile technology practice & dry goods for you to think about every week!


阿里巴巴终端技术
336 声望1.3k 粉丝

阿里巴巴移动&终端技术官方账号。