This article is sponsored by Yu Gang Said Writing Platform

Original author: Sen

Copyright notice: The copyright of this article belongs to the wechat public account Yu Gangshuo

Proguard is introduced

Proguard is best known for its obfuscate capabilities. Proguard can perform shrink, optimize, obfuscate, and Preveirfy to Java class files, according to the Proguard help documentation. Obfuscate is just one of them. The following four functions are briefly introduced:

  1. Shrink: Detects and deletes unused classes, fields, methods, and features.
  2. Optimize: Analyze and Optimize Java bytecode.
  3. Obfuscate: Rename classes, fields, and methods using short, meaningless names.
  4. Preveirfy: Used to prevalidate Java classes (prevalidate is mainly for JME development, there is no prevalidate process in Android, it is turned off by default).

Supplementary notes: According to the proguard – android – optimize. TXT description of optimize the use of the functions in android is a potential risk, there is no guarantee that in all versions of the Dalvik virtual machine run normally, this option is off by default, if open, please to do comprehensive testing. In Android projects, we’ll see this in the build.gradle file under the corresponding Module

 buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}Copy the code

If minifyEnabled is true, Proguard is enabled; if false, Proguard is disabled.

Prouguard’s workflow is shown below:

The Proguard workflow processes the input jars in order of shrink->optimize->obfuscate-> Preveirfy. Jars such as rt.jar for the Java runtime or Android.jar for the Android runtime do not change during the above processing and are only used as dependencies for the input jars. The other four steps are optional. You can add a modifier to the -keep option in the proGuard-rules. pro file where you need to customize the Proguard rule, such as -keep allowallowclass_specification. For example, if you want to use -keep allowcircles, which will be removed if Proguard determines that the template is not being used, you can use the -keep allowcircles modifier to create more circles. If Proguard determines that the template is not being used, you can use the -keep allowcircles modifier to create more circles. Allowoptimization and allowObfuscation only perform the corresponding optimization and obfuscation operations, and their parts are not deleted during the compression phase.

You might wonder how Proguard knows which classes, methods, member variables, etc., are useless. This comes to Entry Points, a series of -keep options that we write in our configuration files (including the default proGuard-Android.txt). Proguard uses these Entry Points as the Entry Points of search for recursive retrieval to determine which parts are not used. Similar to the hotspot VIRTUAL machine’s determination of recyclable objects, the reachable objects are determined from GC Roots. The unreachable objects are recyclable objects. Entry Points are very important, and Proguard’s compression, optimization, and obfuscation functions are based on Entry Points (precheck does not need to be based on this).

During compression, Proguard recursively retrieves from Entry Points and removes unused classes and class members. During subsequent optimization, classes and methods that are not Entry Points are made private, static, or final. Unused parameters are removed, some methods may be marked inline, and non-EntryPoint classes and class members may be renamed, i.e., replaced by meaningless names, in the obtrusion process. The part that we reserved with -keep in the configuration file belongs to the Entry Point and will not be renamed.

Proguard configuration file basis

When it comes to naming, why keep some classes and their members (methods and variables) from being renamed? Because Proguard to class files after a series of processing, can guarantee the function and the original is the same, but in some cases it is not good processing, such as some of our code function depends on their original name, features such as reflection, native calls (such as function signature), if switch to other name, will be to find, If not, the program may crash, or our external provided some functions, we must keep the original name, in order to ensure that other modules relying on these functions can run correctly. This is one of the reasons why we need to configure the -keep option. Another reason is that we need to use -keep to tell the Proguard Entry (any option with -keep will be an Entry Point) to identify unused classes and their members, methods, etc., and delete them. Therefore, We need to configure the options for our project. Of course, Proguard provides not only the -keep option, but also other configuration options such as -dontoptimize to not optimize input Java class files and -verbose to generate obturated mapping files. The following describes the common configuration of the ProGuard file in the app and the instructions that may be used in the project. For more detailed usage, refer to the Proguard help documentation

Write the Proguard configuration file

The first item can be used as the configuration template of Android App (the default proguard-Android. TXT file does not list the configuration), which can be used by almost all apps.

General configuration

# Code obfuscation compression ratio, between 0 and 7, default is 5, generally do not change
-optimizationpasses 5

# mixup the method names in the mixup class
-useuniqueclassmembernames

Optimization allows access to and modification of modifiers and class members
-allowaccessmodification

Avoid confusing inner classes, generics, and anonymous classes
-keepattributes InnerClasses,Signature,EnclosingMethod

Keep the line number when throwing an exception
-keepattributes SourceFile,LineNumberTable

# rename filename to "SourceFile"
-renamesourcefileattribute SourceFile

Keep all class members that implement the Serializable interface
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

Keep the four major components we use, custom applications, etc. These classes are not confused
# because these subclasses can be called externally-keep public class * extends android.app.Activity -keep public class * extends android.app.Appliction -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.PreferenceKeep all classes under support and their inner classes-keep class android.support.** {*; }Keep the inherited support class-keep public class * extends android.support.v4.** -keep public class * extends android.support.v7.** -keep public class  * extends android.support.annotation.**Keep our custom controls (inherited from View) 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); }#Fragment does not need to be registered in androidmanifest.xml and requires additional protection
-keep public class * extends android.app.Fragment

Keep testing relevant code
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**  
Copy the code

The following is the configuration for our App.

1. Entity classes need to be preserved

We need to keep the get and set methods of the entity class (used by reflection). The Boolean get method is isXXX, so don’t forget to keep it.

 -keep public class com.dev.example.entity.** {
    public void set* * * * (); public *** get*(); public *** is*(); }Copy the code

If all entity classes are in one package, the above configuration can be written only once. In practice, however, we divide the package name by business, so we can also configure the package name (entity class must contain “Model”).

-keep public class **.*Model*.** {
    public void set* * * * (); public *** get*(); public *** is*(); }Copy the code

2. Internal class processing

If inner classes are used in your project, keep them.

1. Keep all inner classes written to a class. For example, if class A has an inner class B and B has an inner class C, both B and C will be preserved, and so on. In the case of multiple nesting, the $symbol is used to separate the inner class from its parent. Write code that will be retained (of course, we will not write such a deep level of inner classes), the internal class contains static inner class here, the static inner class does not contain an anonymous inner class, if it was an anonymous inner class, will only retain its methods and member variables (the inheritance of class, or the name of the implementation of the interface will be confused), and if the corresponding class is retained, Interfaces defined in this class are also preserved, {*; } matches all parts of the class.

-keep class com.dev.example.A$* { *; }
Copy the code

2. Keep all inner classes written in an inner class. For example, class A has an inner class B, and all inner classes written in class B are kept. In this case, class B, like class A in the first point above, has A recursive meaning. Also, the name of class B will not be confused, but the methods and member variables will be confused if the methods and member variables of class B are not reserved elsewhere.

-keep class com.dev.example.A$B{$* *; }Copy the code

3. Process the webView

 -keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}
-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.webViewClient {
    public void *(android.webkit.webView, jav.lang.String);
}
Copy the code

4. Keep the native methods of JS calls

If our app involves interacting with H5, we need to keep the native methods of JS calls.

# Keep JavascriptInterface
-keepclassmembers class ** {
    @android.webkit.JavascriptInterface public *;
}
Copy the code

5. Processing of classes containing reflection

Sometimes there are classes in a project that are not entity classes but still use reflection, such as class.forname (” XXX “), which we want to keep. For example, if these classes are in the com.dev. Example package, they can be preserved with the following configuration.

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

If there are subpackages in the package, the subpackage classes will still be confused. If we want to retain the subpackage classes of the package, we can configure as follows (** can match the package and the subpackage, which can also contain subpackages).

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

6. Common custom configurations

1. Keep a specific class

Keep the Test class
-keep public class com.dev.example.Test { *; }
Copy the code

2. Keep subclasses of a class

Keep subclasses that inherit from AbstractClass-keep class * extends com.dev.example.AbstractClass{*; }Copy the code

3. Preserve the implementation class of the interface

Keep classes that implement the Callable interface-keep class * implements Callable{*; }Copy the code

4. Preserve specific sections of the TaskRepository class. Preserve all constructors, variables, and common methods of the TaskRepository class.

-keep class com.dev.example.TaskRepository{ <init>; // Match all constructors <fields>; // Match all fields <methods>; // Match all methods}Copy the code

You can also keep it a little more specific, as shown below

- keepclassmembers com. Dev. Example. TaskRepository {/ / keep the modifier is a public and a parameter (String type) of public constructors <init>(java.lang.String); Public void *(**); public void *(**); Public String getUserName(); }Copy the code

7. Processing of third-party dependent libraries

Here are a few examples of how to use a specific third generation dependency library, which will be configured in the corresponding documentation.

#okhttp-dontwarn com.squareup.okhttp.** -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { *; } -dontwarn okio.**#retroift
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions

# fresco SDK
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
    @com.facebook.common.internal.DoNotStrip *;
}

#rx-dontwarn rx.** -keep class rx.** { *; }#keep GSON stuff
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.** { *; }

#ButterKnife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder{*; } -keepclasseswithmembernames class * { @butterknife.* <fields>; } -keepclasseswithmembernames class * { @butterknife.* <methods>; }#enventbus-keep class org.greenrobot.eventbus.** { *; } -dontwarn org.greenrobot.eventBus.** -keepClassMembers class ** {public void onEvent*(**); }# Bugly-dontwarn com.tencent.bugly.** -keep public class com.tencent.bugly.**{*; }# aliyun push
-keepclasseswithmembernames class ** {
    native <methods>;
}

# QQ share SDK-dontwarn com.tencent.** -keepnames class com.tencent.** {*; }# sina share SDK-dontwarn com.sina.** -keepnames class com.sina.** {*; }# umeng SDK
-keep public class * extends com.umeng.**
-dontwarn com.umeng.**
-keep class com.umeng.** { *; }
Copy the code

other

Another way to configure multi-module projects is to turn off Proguard for sub-Modules and configure all module configuration options in proGuard-rules.pro in our main app. This will make the proGuard configuration file of the main app messy. If the functions of a submodule are no longer needed in the process of service development, you need to find the configurations of the corresponding submodule in the configuration file of the main app and delete them, which is not recommended. Another way is for each module to configure its own configuration file. Note that the way to make a configuration file in a sub-module is as follows:

buildTypes {
        release {
            consumerProguardFiles  'proguard-rules.pro'}}Copy the code

Submodules specify configuration files via consumerProguardFiles, not proguardFiles.

The -dontwarn option will not prompt you with any of the warnings, such as the could not reference class.

This is basically the Proguard configuration section. Proguard operates on class bytecode files. Sometimes we also want to obfuscate resource files. The most mature method is wechat resource obfuscating files. Attach the link: https://github.com/shwenzhang/AndResGuard

Check for confusion and track anomalies

When Proguard is enabled, Proguard outputs the following files each build:

  • Dump. TXT describes the internal structure of all class files in APK.
  • Mapping.txt provides conversions between original and obfuscated class, method, and field names.
  • Seeds.txt lists classes and members that have not been obfuscated.
  • Usage.txt lists the code removed from APK.

These files are stored in the/build/outputs/mapping/release /. We can look at seeds.txt to see if it’s the one we want to keep, and usage.txt to see if it’s deleted incorrectly. The mapping.txt file is very important, because part of our code has been renamed, if there is a bug in this part, the classes or members in the corresponding exception stack information have also been renamed, so it is difficult to locate the problem. We can use the retrace script (retrace.bat on Windows; Retrace.sh) on Mac/Linux. It is located in the /tools/proguard/ directory. This script uses the mapping.txt file and your exception stack file to generate an unscrambled exception stack file so you can see where the problem is. The syntax for using retrace is as follows:

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
Copy the code

Such as:

retrace.bat -verbose mapping.txt obfuscated_trace.txt
Copy the code

This article is a reference to Proguard documentation and several well-written blogs. The purpose of this article is to introduce the use of Proguard on Android and explain some of the points you might encounter in understanding Proguard.

Reference: Prouguard documentation developer.android.com/studio/buil… www.diycode.cc/topics/380 blog.csdn.net/ljd2038/art… Blog.csdn.net/ccpat/artic…

Welcome to follow my wechat public account “Yugang said” to receive first-hand technical dry goods