项目到了快发布的时候,我们必然会对项目打包进行混淆,这样做可以提高别人对我们项目反编译的难度,其实Android的混淆在Android studio中已经帮我们做了一些基本的配置,看了网上了一些讲关于混淆的,都说在android sdk 目录下 \sdk\tools\proguard\proguard-android.txt 下提供了默认的混淆配置,我们打开这个文件查看,发现开头有这样一段注解:

# This file is no longer maintained and is not used by new (2.2+) versions of the
# Android plugin for Gradle. Instead, the Android plugin for Gradle generates the# default rules at build time and stores them in the build directory.

大致的意思就是说这个文件不再维护了,并且在Android Gradle插件2.2+以上不再使用了。相反,Android Gradle插件在构建时生成默认规则,并将它们存储在build目录中。所以说现在混淆的默认规则是由Gradle自动生成的。我们先看一点gradle中的配置:

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

要生成默认规则,上面的minifyEnable一定要改为true,如果为false是不会生成的。我们看到上面有一个输出语句println,这里打印的就是生成的proguard-android.txt文件的位置:

getDefaultProguardFile('proguard-android.txt').exists()

可以通过上面这个输出语句判断文件是否存在,minifyEnable为true时存在的,为false时是不存在的,如果已经存在,为false也是存在的,来看下输出:

E:\AndroidWorkProject\proguardtest\build\intermediates\proguard-files\proguard-android.txt-3.1.0

这里在说下查看输出的位置,对于3.0之前的版本,右下角有个Gradle Console可以查看,我用的是Android studio 3.1,查看位置如下图:

android 让dataclass混淆保留 android 混淆配置_android

在看下生成后的工程目录:

android 让dataclass混淆保留 android 混淆配置_android_02

在build目录下生成了三个混淆文件,可以看到这里多出了一串数字,这个是Gradle插件的版本号,后面会讲到。proguard-android.txt-3.1.0使用这个文件混淆时,是不会对混淆进行优化的,而另外两个是在混淆时会进行优化。

        接下来我们来看下下面这个方法:

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

对于studio是3.0之前的版本,想要看源码需要配置,不会的自己百度。对于这样的代码如果你有点看不太懂,那你就需要补点Groovy的语法了,这里我们先看下下面这个方法:

getDefaultProguardFile('proguard-android.txt')

这里我们直接按住ctr+左键 进入到这个方法内部去瞧一瞧,

public File getDefaultProguardFile(String name) {
    if (!ProguardFiles.KNOWN_FILE_NAMES.contains(name)) {
        extraModelInfo
                .getSyncIssueHandler()
                .reportError(
                        EvalIssueReporter.Type.GENERIC, ProguardFiles.UNKNOWN_FILENAME_MESSAGE);
    }
    return ProguardFiles.getDefaultProguardFile(name, project);
}

这里看到有一个判断,判断在这个集合中是否包含传进来的name,这个集合是通过lambda将一个枚举转换过来的的,我们来看下这个枚举:

public enum ProguardFile {
    /** Default when not using the "postProcessing" DSL block. */
    DONT_OPTIMIZE("proguard-android.txt"),

    /** Variant of the above which does not disable optimizations. */
    OPTIMIZE("proguard-android-optimize.txt"),

    /**
     * Does not disable any actions, includes optimizations config. To be used with the new
     * "postProcessing" DSL block.
     */
    NO_ACTIONS("proguard-defaults.txt"),
    ;

    @NonNull public final String fileName;

    ProguardFile(@NonNull String fileName) {
        this.fileName = fileName;
    }
}

就是上面生成的那三个文件,我们接着往下看getDefaultProguardFile()这个方法:

public static File getDefaultProguardFile(@NonNull String name, @NonNull Project project) {
    if (!KNOWN_FILE_NAMES.contains(name)) {
        throw new IllegalArgumentException(UNKNOWN_FILENAME_MESSAGE);
    }

    return FileUtils.join(
            project.getRootProject().getBuildDir(),
            AndroidProject.FD_INTERMEDIATES,
            "proguard-files",
            name + "-" + Version.ANDROID_GRADLE_PLUGIN_VERSION);
}

这个KNOW_FILE_NAMES还是上面那个集合,这里就是判断传进来的那个name是否是三个文件名中的一个,如果不是,那么会抛异常,我们来看下这个异常:

Supplied proguard configuration file name is unsupported. Valid values are: [proguard-android-optimize.txt, proguard-defaults.txt, proguard-android.txt]

这就是说传进来的文件名必须是这三个文件名中的一个,上面我们看到生成的文件有一个版本号的后缀,在这里我们就可以得到解释了,我们再往下看这个join方法:

public static File join(@NonNull File dir, @NonNull String... paths) {
    if (paths.length == 0) {
        return dir;
    }

    return new File(dir, PATH_JOINER.join(paths));
}

这下就该明白了,这里就是在创建一个文件。看来默认的混淆文件就是这样创建而来的。

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

到这,getDefaultProguardFile()就分析完了,接下来就是是看proguardFiles,这也是个方法,后面的就是方法中的参数,proguard-rules.pro就是我们自己定义的混淆规则,来看看代码:

public BuildType proguardFiles(@NonNull Object... files) {
    checkPostprocessingConfiguration(PostprocessingConfiguration.OLD_DSL, "proguardFiles");
    for (Object file : files) {
        proguardFile(file);
    }
    return this;
}

遍历所有传进来的文件然后执行proguardFile():


public BuildType proguardFile(@NonNull Object proguardFile) {
    checkPostprocessingConfiguration(PostprocessingConfiguration.OLD_DSL, "proguardFile");
    getProguardFiles().add(project.file(proguardFile));
    return this;
}

这里getProguardFiles()返回的是一个List集合,也就是说这里把所有的混淆文件添加进了一个集合中,也就是说这两个混淆文件是共同作用的。

        我们这里来看下Android中的默认混淆规则,也就是proguard-android.txt这个文件中的内容:

-dontoptimize

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# Preserve some attributes that may be required for reflection.
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod

-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
-keep public class com.google.android.vending.licensing.ILicensingService
-dontnote com.android.vending.licensing.ILicensingService
-dontnote com.google.vending.licensing.ILicensingService
-dontnote com.google.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
    native <methods>;
}

# Keep setters in Views so that animations can still work.
-keepclassmembers public class * extends android.view.View {
    void set*(***);
    *** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick.
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

-keepclassmembers class **.R$* {
    public static <fields>;
}

# Preserve annotated Javascript interface methods.
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

# The support libraries contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontnote android.support.**
-dontwarn android.support.**

# This class is deprecated, but remains for backward compatibility.
-dontwarn android.util.FloatMath

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

这里看到有好些规则Android中已经为我们配置了,那么我们在proguard-rules.pro中就没必要在去重新配置了,看到网上的好多配置都在proguard-rules.pro中去重新配置了。

        这里我说一个配置的属性,在proguard-rules.pro中我们配置如下属性:

-assumenosideeffects class android.util.Log {
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

并且把proguard-android.txt中-dontoptimize去掉,那么我们在代码中的log日志将不会打印,当然也可以通过自己写个日志工具类来控制是否输出。

       

再来一个配合proguard-android.txt的proguard-rules.pro的通用配置:

# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5

# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers

# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify

# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses

# 避免混淆泛型
-keepattributes Signature

# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#############################################
#
# Android开发中一些需要保留的公共部分
#
#############################################

# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-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.Preference
-keep public class * extends android.view.View

# 保留support下的所有类及其内部类
-keep class android.support.** {*;}

# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**

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

# 保留Parcelable序列化类不被混淆
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# 保留Serializable序列化的类不被混淆
-keepnames class * implements java.io.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();
}

# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
    void *(**On*Event);
    void *(**On*Listener);
}

# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
#-assumenosideeffects class android.util.Log {
#    public static int v(...);
#    public static int i(...);
#    public static int w(...);
#    public static int d(...);
#    public static int e(...);
#}


当我们这样配置编译报错时,如果是没有忽略warning,我么可以加上:

-ignorewarnings

好了,到这就差不多了。