1. Proguard is introduced

The Android SDK comes with obfuscation tool Proguard. It is located under the SDK root directory \tools\ ProGuard. ProGuard is a free Java class file shrink, optimize, obfuscation, and prevalidator. It can detect and remove unused classes, fields, methods, and properties. It optimizes bytecode and removes unused instructions. It can rename classes, fields, and methods with short, meaningless names. Finally, prevalidates Java6 or the processed code for Java MicroEdition. If obfuscation is enabled, Proguard obfuscates all code, including third-party packages, by default. However, some code or third-party packages cannot be obfuscated, which requires manual obfuscation rules to keep the parts that cannot be obfuscated.

2. Proguard role

“Obfuscation” in Android can be divided into two parts. One is optimization and obfuscation of Java code, which is realized by proGuard obfuscation. The other part is resource compression, which removes unused resources from projects and dependent libraries (resource compression is not strictly related to confusion, but is generally covered together).

2.1 Code Confusion

Shrinking: Enabled by default to reduce application size, remove unused classes and members, and perform again after the optimization action (which might expose unused classes and members again).

-dontshrink turns off compressionCopy the code

Optimization: Enabled by default, optimizations are performed at the bytecode level to make applications run faster.

- Dontoptimize - OptimizationPasses n Specifies the number of times proGuard has iteratively optimized the code. Android passes 5Copy the code

Obfuscation: This feature is enabled by default to make it more difficult to decomcompile. Classes, functions, and variables are randomly named with meaningless code names such as A, B, C… Or something like that, unless you keep it protected.

- Dontobfuscate closes obfuscateCopy the code

These functions are enabled by default. You only need to configure the corresponding rules to disable them. In confusion after the default project directory app/build/outputs/mapping/release generated under a mapping. TXT file, this is the rule of confusion, we can according to the file read the obfuscated code back to the source of this code, so this file is very important, pay attention to protect good. In principle, the more chaotic the code, the better, but there are some areas we should avoid, or the program will run wrong.

2.2 Resource Compression

Resource compression removes unused resources from projects and dependent libraries. This can be useful in reducing the size of APK packages and is generally recommended. Set shrinkResources to true in the build.grade file. Note that resource compression will only take effect if code compression is enabled with minifyEnabled True. Resource compression consists of merging resources and removing resources. In the Merge Resource process, resources with the same name are regarded as duplicate resources and merged. Note that this process is not controlled by the shrinkResources property and cannot be disabled. Gradle must do this because it will cause an error if there are resources with the same name in different projects. Gradle looks for duplicate resources everywhere:

  • src/main/res/The path
  • Different build types (Debug, Release, and so on)
  • Different build channels
  • The third-party libraries that the project depends on merge resources in the following order of priority:
Dependencies -> Main -> Channels -> Build typeCopy the code

For example, if a duplicate resource exists in the main folder and in different channels, Gradle chooses to keep the resource in the channel. At the same time, if duplicate resources occur at the same level, such as SRC /main/res/ and SRC /main/res2/, Gradle cannot complete the resource merge and reports a resource merge error. The “Remove resource” process is known by name, but it is important to note that, like code, obfuscating resource removal can also define which resources need to be retained, as described below.

3. The Proguard rules

3.1 Basic Instructions

  • -ignorewarning: Indicates whether to ignore warnings
  • – OptimizationPasses n: specifies the compression level of the code (between 0 and 7, the default is 5)
  • – dontusemixedcaseclassnames: whether to use mixed case (Windows case-insensitive, suggest to join)
  • – dontskipnonpubliclibraryclasses: whether to confuse the public library class
  • – dontskipnonpubliclibraryclassmembers: whether to confuse the public libraries of the members of the class
  • -dontpreverify: Indicates whether to perform preverification during obfuscation. (Android does not require preverification.
  • -verbose: Whether to record logs when confusion occurs (mapping files are generated after confusion)
  • – obfuscationDictionary dictionary_path: specifies the external fuzzy dictionary
  • – classobfuscationdictionary dictionary_path: specify the class fuzzy dictionary
  • – packageobfuscationdictionary dictionary_path: specify the package fuzzy dictionary
  • -optimizations ! code/simplification/arithmetic,! field/,! class/merging/,! Code /allocation/variable: algorithm to use in case of confusion (Google Recommended algorithm)
  • -libraryjars libs(*.jar;) Add supported JARS (import all jar packages under LIBS)
  • – renamesourcefileattribute SourceFile: will source file renamed “SourceFile string.
  • – KeepAttributes Annotation: Keeps annotations from being confused
  • -keep class * extends java.lang.annotation.Annotation {*; } : Keep annotations unconfused
  • -keep interface * extends java.lang.annotation.Annotation { *; } : Keep annotations unconfused
  • – KeepAttributes Signature: Keeps generic types from being confused
  • – KeepAttributes EnclosingMethod: Keeps reflections unconfused
  • – KeepAttributes Exceptions: Keeps Exceptions from being confused
  • -keepattributes InnerClasses: keeps InnerClasses from being confused
  • – keepattributes SourceFile LineNumberTable: throw an exception when keep code line number

3.2 Reserved Options

  • – keep [, modifier,… Class_specification: Specify which classes and class members you want to keep (as a public library, you should keep all publicly accessible public methods)
  • – keepclassmembers [, modifier,… Class_specification: Specifies the class members that you want to keep: variables or methods
  • – keepclasseswithmembers [, modifier,… Class_specification: specify the classes and members to be retained, provided that all members of the specified class exist.
  • – keepNames class_specification: specifies the classes and class members whose names are to be retained, provided that they are not deleted during compression. [-keep allowallowclass_specification]
  • – keepclassmemberNames class_specification: specifies the classmember whose name is to be retained, if it is not deleted in the compression phase. It is only used for blur processing. [-keepclassmembers allowshrinking class_specification]
  • – keepclasseswithmembernames class_specification: keep the name of the class members is specified, the premise is that during the compression stage after the specified class members are present, only used for fuzzy processing. [-keepclasseswithmembers allowShrinking class_specification]
  • -printseeds [filename] : Specifies an exhaustive list of classes and class members matched by the various -keep options. The list is printed to standard output or to a given file. This list can be used to verify that the expected class members are actually found, especially if you use wildcards.

4. Description of the Keep command

The command role
-keep Keep classes and class members from being removed or renamed
-keepnames Keep classes and class members from being renamed
-keepclassmembers Keep class members from being removed or renamed
-keepclassmembernames Keep class members from being renamed
-keepclasseswithmembers Preserves the class and member that owns the member, preventing it from being removed or renamed
-keepclasseswithmembernames Keep the class and member that owns the member from being renamed

The format of commands to keep elements from participating in obfuscation rules:

[Hold command] [class] {[member]}Copy the code

“Class” represents a class-related qualification that will eventually locate some classes that meet that qualification. Its contents can be used:

  • Concrete classes
  • Access modifier (Public, protected, private)
  • The wildcard*, matches any length of characters, but does not contain the package name delimiter (.)
  • The wildcard**, matches characters of any length and contains the package name delimiter (.)
  • extends, that is, you can specify the base class of a class
  • implementMatches a class that implements an interface
  • $, the inner class “member” represents the qualification related to the class member, which will eventually locate some class members that meet the qualification. Its contents can be used:
  • <init>Matches all constructors
  • <fields>Match all domains
  • <methods>Match all methods
  • The wildcard*, matches any length of characters, but does not contain the package name delimiter (.)
  • The wildcard**, matches characters of any length and contains the package name delimiter (.)
  • The wildcard* * *Matches any parameter type
  • ...Matches any type of argument of any length. Such as void test (…). I can match any void test(String a) or void test(int A, String b).
  • Access modifier (Public, protected, private)

4.1 Do not confuse a class

    -keep public class com.android.proguard.example.Test { *; }
Copy the code

4.2 Do not confuse all classes of a package

    -keep class com.android.proguard.example.** { *; }
Copy the code

4.3 Do not confuse subclasses of a class

    -keep public class * extends com.android.proguard.example.Test { *; }
Copy the code

4.4 Do not confuse all classes and their members whose class names contain “Model”

-keep public class **.*model*.** {*; }Copy the code

4.5 Do not confuse the implementation of an interface

    -keep class * implements com.android.proguard.example.TestInterface { *; }
Copy the code

4.6 Do not confuse the constructor of a class

    -keepclassmembers class com.android.proguard.example.Test {
        public <init>();
    }
Copy the code

4.7 Do not confuse specific methods of a class

    -keepclassmembers class com.android.proguard.example.Test {
        public void test(java.lang.String);
    }
Copy the code

4.8 Do not confuse inner classes of a class

    -keep class com.android.proguard.example.Test$* {
            *;
     }
Copy the code

5. Precautions for Proguard

5.1 Keep basic components unconfused

-keep public class * extends android.app.Fragment -keep public class * extends android.app.Activity -keep public class *  extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.PreferenceCopy the code

5.2 Keep the classes required by Google native services from being confused

    -keep public class com.google.vending.licensing.ILicensingService
    -keep public class com.android.vending.licensing.ILicensingService
Copy the code

5.3 Support Package Rules

    -dontwarn android.support.**
    -keep public class * extends android.support.v4.**
    -keep public class * extends android.support.v7.**
    -keep public class * extends android.support.annotation.**
Copy the code

5.4 Keep native methods from being confused

    -keepclasseswithmembernames class * { # # # #
        native <methods>;
    }
Copy the code

5.5 Keep custom controls (inherited from Views) unconfused

    -keep public class * extends android.view.View { # # # #
        *** get*();
        void set* * * * (); public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); }Copy the code

5.6 Preserve the constructor of the specified format from confusion

    -keepclasseswithmembers class * {
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
    }
Copy the code

5.7 Method parameters retained in the Activity are View methods (to avoid affecting onClick in the layout file)

    -keepclassmembers class * extends android.app.Activity {
        public void *(android.view.View);
    }
Copy the code

5.8 Keep enumeration Enum classes from being confused

    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
Copy the code

5.9 Keep all classes and methods under R(resource) unconfused

    -keep class **.R$* { *; }
Copy the code

5.10 Keep Parcelable serialized classes from being obfuscated (note: AIDL files cannot be obfuscated)

    -keep class * implements android.os.Parcelable {
        public static final android.os.Parcelable$Creator *;
    }
Copy the code

5.11 Classes that need to be serialized and deserialized cannot be confused (note: Classes used by Java reflection cannot be confused either)

    -keepnames class * implements java.io.Serializable
Copy the code

5.12 Keep Serializable class members from being confused

-keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; ! static ! transient <fields>; ! private <fields>; ! private <methods>; private void writeObject(java.io.ObjectOutputStream); private voidreadObject(java.io.ObjectInputStream);
        java.lang.Object writeReplace();
        java.lang.Object readResolve();
    }
Copy the code

5.13 Keep the BaseAdapter class from being confused

    -keep public class * extends android.widget.BaseAdapter { *; }
Copy the code

5.14 Keep the CusorAdapter class from being confused

    -keep public class * extends android.widget.CusorAdapter{ *; }
Copy the code

5.15 Keep the classes used by reflection and the classes interacting with JavaScript unconfused

6. Customize resource retention rules

6.1 keep the XML

With shrinkResources true enabled, all unused resources are removed by default. If you need to define which resources must be reserved, create an XML file in the res/raw/ path, such as keep.xml. You can define resource retention requirements by setting the following properties:

  • tools:keepDefine which resources need to be reserved (separated by commas)
  • tools:discardDefine which resources need to be removed (separated by commas)
  • tools:shrinkModeTurn on strict mode when code is passedResources.getIdentifier()When retrieving and using resources using dynamic strings, ordinary resource reference checking can be problematic. For example, the following code causes all resources starting with “img_” to be marked as used.
    String name = String.format("img_%1d", angle + 1);
    res = getResources().getIdentifier(name, "drawable", getPackageName());
Copy the code

We can set Tools :shrinkMode to strict to enable strict mode so that only resources that are actually used are reserved. The above is the configuration related to custom resource retention rules, as an example:

    <?xml version="1.0" encoding="utf-8"? >
    <resources xmlns:tools="http://schemas.android.com/tools"
        tools:keep="@drawable/img_*,@drawable/ic_launcher,@layout/layout_used*"
        tools:discard="@layout/layout_unused"
        tools:shrinkMode="strict"/>
Copy the code

6.2 Removing an Alternative Resource

Some alternative resources, such as strings. XML with multi-language support and layout. XML with multi-resolution support, can be removed by resource compression when they are not needed and do not want to be deleted. We use the resConfig property to specify the properties that need to be supported, for example

    android {
        defaultConfig {
            ...
            resConfigs "en"."zh"}}Copy the code

Other language resources that are not explicitly declared are removed.

7. Use Proguard

7.1 Enabling Obfuscation

Open the build.gradle file in the executable project Module for editing:

android {
    ......
    defaultConfig {
        ......
    }
    buildTypes {
        release {
            minifyEnabled true      // Turn on code obfuscation
            zipAlignEnabled true    // Enable Zip compression optimization
            shrinkResources true    // Remove unused resources
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}... }Copy the code
  • MinifyEnabled: whether to obfuscate the code
  • ZipAlignEnabled: Indicates whether to optimize Zip compression
  • ShrinkResources: Whether to remove unused resources
  • ProguardFiles: confusion rule configuration file
    • Proguard-android. TXT: Automatic import rules for AndroidStudio by default. This file is located in the android SDK root directory \tools\ proGuard \proguard-android. TXT. Here are some general code rules that should not be confused.
    • Proguard-rules.pro: Obturation rules that are specific to your project, located at the root of each Module of your project, and that you need to write yourself.

7.2 Writing obfuscation Rules

# --------------------------------------------基本指令区--------------------------------------------#
-ignorewarning                                      # 是否忽略警告
-optimizationpasses 5                               # 指定代码的压缩级别(在0~7之间,默认为5)
-dontusemixedcaseclassnames                         # 是否使用大小写混合(windows大小写不敏感,建议加入)
-dontskipnonpubliclibraryclasses                    # 是否混淆非公共的库的类
-dontskipnonpubliclibraryclassmembers               # 是否混淆非公共的库的类的成员
-dontpreverify                                      # 混淆时是否做预校验(Android不需要预校验,去掉可以加快混淆速度)
-verbose                                            # 混淆时是否记录日志(混淆后会生成映射文件)

#指定外部模糊字典
-obfuscationdictionary dictionary1.txt
#指定class模糊字典
-classobfuscationdictionary dictionary1.txt
#指定package模糊字典
-packageobfuscationdictionary dictionary2.txt

# 混淆时所采用的算法(谷歌推荐算法)
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable

# 添加支持的jar(引入libs下的所有jar包)
-libraryjars libs(*.jar;)

# 将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile

# 保持注解不被混淆
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}

# 保持泛型不被混淆
-keepattributes Signature
# 保持反射不被混淆
-keepattributes EnclosingMethod
# 保持异常不被混淆
-keepattributes Exceptions
# 保持内部类不被混淆
-keepattributes Exceptions,InnerClasses
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# --------------------------------------------默认保留区--------------------------------------------#
# 保持基本组件不被混淆
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference

# 保持 Google 原生服务需要的类不被混淆
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# Support包规则
-dontwarn android.support.**
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**

# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View {
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# 保留指定格式的构造方法不被混淆
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# 保留在Activity中的方法参数是view的方法(避免布局文件里面onClick被影响)
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保持R(资源)下的所有类及其方法不能被混淆
-keep class **.R$* { *; }

# 保持 Parcelable 序列化的类不被混淆(注:aidl文件不能去混淆)
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# 需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)
-keepnames class * implements java.io.Serializable

# 保持 Serializable 序列化的类成员不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# 保持 BaseAdapter 类不被混淆
-keep public class * extends android.widget.BaseAdapter { *; }
# 保持 CusorAdapter 类不被混淆
-keep public class * extends android.widget.CusorAdapter{ *; }

# --------------------------------------------webView区--------------------------------------------#
# WebView处理,项目中没有使用到webView忽略即可
# 保持Android与JavaScript进行交互的类不被混淆
-keep class **.AndroidJavaScript { *; }
-keepclassmembers class * extends android.webkit.WebViewClient {
     public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);
     public boolean *(android.webkit.WebView,java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient {
     public void *(android.webkit.WebView,java.lang.String);
}

# 网络请求相关
-keep public class android.net.http.SslError

# --------------------------------------------删除代码区--------------------------------------------#
# 删除代码中Log相关的代码
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}


# --------------------------------------------可定制化区--------------------------------------------#
#---------------------------------1.实体类---------------------------------



#--------------------------------------------------------------------------

#---------------------------------2.与JS交互的类-----------------------------



#--------------------------------------------------------------------------

#---------------------------------3.反射相关的类和方法-----------------------



#--------------------------------------------------------------------------

#---------------------------------2.第三方依赖--------------------------------



#--------------------------------------------------------------------------


Copy the code