Crack down on project corruption | proguard control

Author: Liu Tianyu (Qian Feng)

Engineering corruption is a very difficult problem in the process of app iteration, involving extensive and detailed details, which has a relatively "hidden" and indirect impact on R & D Efficiency & experience, Engineering & product quality, stability, package size and performance. Generally, it will not cause unbearable obstacles, but it often jumps out and leads to "labor pains". It is a bit like decayed teeth or wisdom teeth. It is impossible not to pull them out to a certain extent. However, the difference is that the corruption of the project is difficult to be cured by one-time "removal". After any "removal", effective sustainable treatment schemes are needed to form a normalized anti-corrosion system.

From the perspective of Engineering corruption disassembly, it is the corruption of the engineering structure itself and various "elements" (manifest, code, resource, so, configuration) in the code engineering that makes up the app. In recent years, Youku architecture team has continued to think, practice and manage, and precipitated some technologies, tools and schemes. Now it is classified and summarized one by one, supplemented by the explanation of relevant field knowledge, and sorted into a series of technical articles on shooting at engineering corruption, which will be shared with you. I hope more students will join in this protracted war against project corruption.

This article, the first in a series of articles, will focus on the java code proguard, a subdivision. Direct fire on the corruption of the project!

In the field of Android (Java) development, the general reference to "code Proguard" refers to the use of Proguard tools to cut, optimize and confuse java code, so as to realize tree shaking, code logic optimization and symbol (class, variable and method) confusion. Proguard processing process has an important impact on apk construction time, product controllability (runtime stability), package size and performance.

Many times, developers will use "confusion" to refer to the whole Proguard processing. Although it is inaccurate, it is understood in combination with the context. As long as there is no ambiguity, it is harmless. It is worth noting that google has officially 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 first briefly introduces some basic knowledge to facilitate you to have a clear understanding of proguard.

Function introduction

The three core functions of Proguard are as follows:

  • Crop. Through the overall static analysis of all code reference relationships, detect and remove useless classes, variables, methods and attributes. It plays an important role in reducing the final apk;
  • Optimize. This is the most complex part of the whole Proguard processing process. Through in-depth analysis of the code execution logic, remove useless code branches, method parameters and local variables, inline methods / classes, and even optimize the instruction set, which contains dozens of optimization items in total. On the one hand, it can reduce the occupation of code size. On the other hand, it is also the most important to reduce the execution time of runtime methods;
  • obfuscate. By shortening the names of classes, variables and methods, the occupation of 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 scheme to increase the difficulty of apk anti cracking.

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

Proguard process

Among them, app classes include all java codes in application project, sub project project, external dependency aar/jar, local jar and flat dir aar. library classes include android framework jar, legacy jars and other codes that are only required during compilation. The runtime is provided by the system and will not be packaged into apk.

Configuration item

Proguard provides powerful configuration items to customize the whole process. Here, it is divided into global configuration and keep configuration. Note that R8 cancels the support for most global configurations in order to maintain the consistency and controllability of the processing process and better processing effect.

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, turn off the clipping function;
  • -whyareyou keep ing. Why is the target class, variable and method specified "kept" but not cut out in apk. Note that the results given by R8 and Proguard have different meanings. Let's look at the comparison:
# Example: the class TestProguardMethodOnly is directly "keep" by the keep rule. In one way of TestProguardMethodOnly, the method in the TestProguardFieldAndMethod class is invoked.

# The result given by Proguard is the shortest path, that is, if multiple keep rules / references lead to, only the information of the shortest path will be given
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.
# Interpretation of results: 
# 1. “is kept by a directive in the configuration.”, TestProguardMethodOnly is directly "kept" by the keep rule
# 2. "is invoked by xxxx", TestProguardFieldAndMethod is called by TestProguardMethodOnly, resulting in "keep"; "is kept by a directive in the configuration.", TestProguardMethodOnly is directly "kept" by the keep rule


# The result given in R8 is which keep rule directly hits the class, that is, if the class is called by other reserved classes, but there is no keep rule directly corresponding to this class, the result given here is "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
# Interpretation of results: 
# 1. "is referenced in keep rule: xxx", TestProguardMethodOnly is directly "kept" by this specific rule. However, if multiple rules "keep" this class, only one keep rule will be displayed here.
# 2. "Nothing is keeping xxxx", TestProguardFieldAndMethod is not directly "kept" by the keep rule

2. Optimize configuration

  • -dontoptimize. After specifying, turn off the optimization function;
  • -optimizationpasses. Optimization times. Theoretically, the more optimization times, the better the effect. Once there is no effect after an optimization, the next round of optimization will be stopped;
  • -optimizations. Please refer to the Proguard document for specific optimization items. The following is a Proguard processing log that you can easily find. Let's feel the following optimization items:

optimize item display

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

3. Confusion configuration

  • -dontobfuscate. When specified, turn off the obfuscation function;
  • other. Including - apply mapping, - obfuscationdictionary, - use unique class member names, don't use mixed case class names and other configuration items, which are used to finely control the confusion processing process. For details, please refer to the documentation.

keep configuration

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

Here, we can think about a question: if there are no keep rules in the apk construction process, will all the code be cut out? The answer is yes. In the end, there will be no code in apk. Some students may say that I created an app project with Android Studio and started Proguard, but did not configure any keep rules. Why did apk contain some code in the end? This is because Android Gradle Plugin will automatically generate some confusion rules in the process of building apk. The source of all keep rules will be discussed in later chapters.

OK, go back to keep configuration. The rules supported by keep configuration are very complex. Here, they are divided into the following categories:

1. Directly retain classes, methods and variables;

  • -keep. The reserved classes, methods and variables are not allowed to shrink or obfuscate;
  • -keepnames. Equivalent to - keep, allowshrinking. Retain classes, methods and variables, allow shrink, and if it is finally retained (other keep rules or code calls), obfuscate is not allowed;

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

  • -keepclassmembers. The reserved variables and methods are not allowed to shrink or obfuscate;
  • -keepclassmembernames. Equivalent to - keepclassmembers, allowshrinking. The reserved variables and methods are allowed to shrink. If they are finally retained, obfuscate is not allowed;

3. If the methods / variables meet the specified conditions, the corresponding classes, variables and methods are retained;

  • -keepclasseswithmembers. The reserved classes, methods and variables are not allowed to shrink or obfuscate;
  • keepclasseswithmembernames. Equivalent to - keepclasseswithmembers, allowshrinking. Classes, methods and variables are reserved. shrink is allowed. If they are finally retained, obfuscate is not allowed.

The format of the complete keep rule is as follows. Feel the complexity below:

 -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 locations support different degrees of wildcards, which will not be described in detail

In practical work, very complex keep rules are generally not used, so there is no need to deliberately learn the complete usage. When encountered, you can understand it by looking up the document. End this section with an interesting example.

===================== Examples =====================
# Example class:
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 rule:
-keepclasseswithmembers class com.example.myapplication.proguard.** {
    *;
}

# Question: which sample classes will be "reserved" due to the above keep rule?
# Answer: TestProguardFieldOnly and TestProguardFieldAndMethod

Supporting documents

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

Supporting documents

Configuration item collection

Configuration item collection, 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 collection.

Open this output through the 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 "reserved" by the keep rule. Note that classes, variables and methods that are called by other reserved methods, resulting in indirect "reservation" are not included in the result file.

Open this output through the configuration item - printfeeds < filepath >, for example - printfeeds 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 results (usage.txt)

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

Open this output through the 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 cropped, only the fully qualified name of the class is listed; If the class is not cropped, but the variables and methods in the class are cropped, the class name will be listed here, and then the cropped variables and methods will be listed.

Confusion results (mapping.txt)

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

Open this output through the 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 Example: list all classes that are reserved and obfuscate the 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

===================== R8 Example: only the reserved and confused classes, variables and methods are listed =====================
# 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 between Proguard and R8. In the actual interpretation, we need to pay attention to.

engineering application

After understanding the basic knowledge of proguard and having an overall "framework", let's take a look at some matters that need to be learned in order to better use proguard in practical engineering. This section will not cover the most basic usage, which can be easily found in official documents and various articles.

Tool selection

First of all, see what tools you can choose. For Android development, there are two tools to choose from: Proguard and R8 (a long time ago, there was a built-in code cutting tool of AGP - Android Gradle Plugin, which is completely outdated and no longer listed). The latter is the substitute of Proguard tool developed by google, which is better than Proguard tool in cutting and optimization processing time and 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 compatible with Proguard to the greatest extent possible. Generally, app s can smoothly switch from Proguard to R8 normal mode;
  • Full mode. In terms of optimization strategy, a more radical scheme is adopted. Therefore, compared with Proguard, additional keep rules may be required to ensure code availability. The opening mode is gradle In the properties file, add the configuration: Android enableR8. fullMode=true.

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

Custom configuration

I talked a lot about configuration items. How to add custom configuration rules in a specific project? Most students should think that this problem can't be simpler. Let's change it and finally participate in the configuration of the processing process. Where do we come from?

For the confusion rules generated by AAPT, let's take a look at some examples to help you understand which keep rules have been automatically added 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); }

You can see that the function name corresponding to the onClick attribute value in layout cannot be confused, and a rule that is easy to cause excessive keep will be generated. Therefore, this method is not recommended in actual code.

Special attention should be paid to the configuration carried in the sub project / external module. If it is not handled carefully, it will bring unexpected results.

Governance practice

The first two chapters explain the basic knowledge and engineering application of proguard. I believe you have formed a preliminary overall understanding of proguard. Due to the wide sources of configuration items, especially the existence of 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 can be easily retained. In engineering practice, with the continuous iteration of app, the following two types of problems will be encountered:

  • Global configuration, which has been modified unexpectedly. Whether to mix up, whether to cut, optimization times, optimization type, etc. once modified, the code will change greatly, affecting the stability, package size and performance;
  • Keep configuration is increasing and gradually corrupting. The number of keep rules is non linearly proportional to the time-consuming of proguard in the construction process (removing useless / redundant keep rules can improve the construction speed). Too extensive keep rules will increase the package size and the code cannot be optimized, which will affect the runtime performance.

"If you want to do a good job, you must first sharpen your tools". Before the actual treatment, the detection tools are developed respectively. Based on the test results provided by the tool, carry out governance work respectively. (the tools involved in this article are all part of Youku's self-developed "onepiece detection and Analysis Kit")

Global configuration

Global configuration detection capability (tool), which provides proguard global configuration detection capability, and timely senses the inconsistency between the value of the target configuration item and the white list based on the white list mechanism. At the same time, it provides options to terminate the construction process and give a prompt when the global configuration changes unexpectedly.

When there is a global configuration inconsistent with the white list, the inconsistent configuration items will be listed in the generated detection result file. The example 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]

The key to this protection is to avoid the unexpected change of the configuration more than once.

keep configuration

The governance 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 no effect on the final treatment 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 included by one or more existing rules. This will lead to unnecessary configuration resolution and increase the processing time (each keep rule will be used to match all class es);
  • Excessive rules. Beyond the necessary keep scope, unnecessary classes, variables and methods are reserved. Here, it also includes the case that only keepnames is needed but keep directly;
  • Precise rules. The necessary rules to follow the minimum reservation principle. There is no need to deal with it, but it should be noted that for the self-developed business code in the app, try to use the @ keep annotation provided in support or Android x to make sure that the keep rules are put together with the code.

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

Comparison of governance difficulty of keep rules

1. Analysis

  • Useless. By matching each keep rule with each class, you can determine whether this class has "influence". The difficulty of this matching mainly comes from the complexity of keep rules and the consistency with the matching results of proguard;
  • Redundancy. If it is a rule, the effect is completely "included" by other rules. This can first calculate the impact of each keep rule on each class, and finally find out that the "retention" range is the same or has a "inclusion" relationship, which can be realized in theory. However, when a rule is "included" by other rules, the detection complexity will become very high;
  • Excessive. This is basically impossible to detect accurately, because which classes, variables and methods should be retained should be judged by "how to use them at runtime". If excessive rules can be detected, all keep rules need not be added manually in theory;

2. Handle

  • Useless. You can delete it directly;
  • Redundancy. Delete one or more rules, or merge several rules;
  • Excessive. Add qualifiers, rewrite rules, etc. Need to have a clear understanding of the expected effect and master the keep rules;

3. Verify

  • Useless. It has no effect on the final cutting and confusion results. Verify the "clipping result" and "confusion result" in the auxiliary documents. In order to further confirm the impact, you can also compare and verify apk itself;
  • Redundancy. Like useless rules, they have no impact on the processing results, and the verification methods are the same;
  • Excessive. It has an impact on the final cutting, optimization and confusion results. It needs to be verified by functional regression.

In terms of 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 is 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 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> ( ... ) ; }    // This is the keep rule. The number in [x] indicates the number of modules hit by the keep rule
|-- [1] com.youku.android:moduleOne:1.21.407.6     // This is the keep hit module. The number in [x] indicates the number of hit classes in the module
|   |-- com.youku.android.widget.TextSetView    // This is the hit class in the 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
......

3. [detection] class is hit detected by keep rule. Which keep rules hit each class (and its module). Compared with - whyareyoukeeping, this detection focus class is directly "affected" by which keep rules.

* com.youku.arch:ModuleOne:2.8.15   // This is the maven coordinate of the module
|-- com.youku.arch.SMBridge    // This is the name of the class. The following is a list of keep rules for this class
|   |-- -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 missed 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, it will be labeled [ignored]

In addition, a comparative analysis tool of "clipping results" and "confusion results" is provided to verify the cleaning results of useless / redundant keep rules.

===================== Comparison of cutting results =====================
*- [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

===================== Comparison of confusion results =====================
*- [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 guest, the governance baseline version, has 3812 keep rules in total. Through the analysis tool, it is found that 758 (20%) of them miss any class, which belongs to useless rules. 700 of them were cleaned up, and by comparing the "cutting results" and "confusion results", it was ensured that they had no impact on the final apk. Most of the rest comes from the rules automatically generated when AAPT compiles resources, but the classes referenced in the resources do not exist in apk, resulting in the uselessness of keep rules. To clean up these rules, you need to delete the references to these nonexistent classes in the resource and add them to the white list for the time being.

# The non-existent class referenced in the layout will not cause construction failure during apk compilation, but the corresponding keep rules will still be generated.
# Once the layout is "loaded" at runtime, it will throw an exception that the Java class cannot find.

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

# The generated keep rule is: - keep class com example. myapplication. NonExistView { <init> ( ... ) ; }

For redundant rules and excessive rules, preliminary small batch trial cleaning is carried out, which is highly complex and difficult to control the risk. Batch cleaning is not carried out first, and then it is gradually cleaned up later.

keep rule distribution & cleanup results

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, implement centralized control in the application project (Youku has disabled the consumer Proguard of external modules), isolate the configuration files according to the team, and formulate the keep rule access mechanism; On the other hand, take the useless keep configuration as a bayonet item, deploy it in the version iteration process, and enter the normalization management stage.

Governance panorama

Finally, a panorama of proguard corruption control is given:

Proguard governance panorama

What else can I do

Other subdivisions of the project corruption are still under way. For proguard governance, on the one hand, in terms of the detection ability of tools, we will explore "redundant keep rules" and "excessive keep rules"; On the other hand, the clean-up of the stock keep rules is not achieved overnight. There is a heavy task and a long way to go. We should encourage you.

[reference documents]

Focus on Alibaba mobile technology WeChat official account, 3 mobile technology practices dry cargo per week to give you thought!

Keywords: Android

Added by sig on Sat, 12 Feb 2022 01:20:45 +0200