本文共 9890 字,大约阅读时间需要 32 分钟。
项目到了快发布的时候,我们必然会对项目打包进行混淆,这样做可以提高别人对我们项目反编译的难度,其实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,查看位置如下图:
在看下生成后的工程目录:
在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;}# 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 ;}# Preserve annotated Javascript interface methods.-keepclassmembers class * { @android.webkit.JavascriptInterface ;}# 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 ;}-keepclasseswithmembers class * { @android.support.annotation.Keep ;}-keepclasseswithmembers class * { @android.support.annotation.Keep (...);}
这里看到有好些规则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当我们这样配置编译报错时,如果是没有忽略warning,我么可以加上:(android.content.Context); public (android.content.Context, android.util.AttributeSet); public (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 ; !private ; !private ; 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(...);#}
-ignorewarnings
好了,到这就差不多了。