Android Studio如何做混淆
概述
ProGuard是一个Java类文件压缩器、优化器、混淆器、预校验器:
- 压缩阶段会检测和移除未使用的类、字段、方法、属性。ProGuard以递归的方式检查并决定哪些类和类成员是被用到的,而其他没有用到的类和类成员就会被丢弃。
- 优化阶段会分析并优化方法的字节码。ProGuard会进一步优化代码。其他优化包括不是入口点的类或方法可能会变成private、final、static,而没有使用的参数可能会被移除等。
- 混淆阶段会用简短的无意义的名称来重命名剩下的类名、字段名、方法名。ProGuard会重命名那些不是入口点(说白了入口点就是公开的,public的)的类和类成员,会保留那些入口点(public的),保证他们能够正确地访问。
进行完以上三个段后,我们的apk包会更小、更高效,并且更难进行反向工程。
- 预校验阶段会将预验证信息添加到类中,这是Java Micro Edition所必需的,或者可以缩短Java 6的启动时间。只有这一阶段不需要知道入口点的。
ProGuard读取Jar包(或者是wars、ears、zips或者目录。然后开始压缩资源、优化代码、混淆代码和预校验它们。
前面这四个步骤都是可选的。
proguard-rules.pro文件配置
为了决定哪些代码要保留、哪些代码要丢弃或混淆,我们必须在配置文件中指明。proguard-rules.pro文件可以配置的选项:
- 输入输出选项
-include {filename} 从给定的文件中递归读取配置选项
@filename '-include filename'的简写
-basedirectory {directoryname} 指定为这些配置参数或配置文件中的相对文件提供基础目录
-injars {class_path} 指定要处理的应用程序的jar,war,ear和目录
-outjars {class_path} 指定处理完后要输出的jar,war,ear和目录的名称,我们应该避免输出的文件重写输入文件,不使用这个选项的话就可以做到,就不会有任何jar包被重写。
-libraryjars {classpath} 指定要处理的应用程序jar,war,ear和目录所需要的程序库文件,这里的文件就不会被包含进输入jars中。
-dontskipnonpubliclibraryclasses 指定跳过非公共类当读取库jar包时,加速处理和减少ProGuard的内存使用。默认情况下,ProGuard会读取非公共和公共库类文件。
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可见的库类的成员。
-skipnonpubliclibraryclasses 指定路过的非public类,当读库文件,加速处理和减少ProGuard的内存占用。默认情况ProGuard读取非public和public库文件。但是有些库包含了扩展自public库类的非public库类,如果使用了此选项就会报错,所以建议不要使用此选项。
-keepdirectories [directory_filter]指定要在输出jars文件中保留的目录。默认情况下,目录入口都会被移除,可以减少jar包的大小。如果不加过滤目录,只指定了这个选项,则全部目录都会被保留,否则只保留过滤目录。
-target version 指定版本号,用于设置被处理好的类文件。默认,类文件的版本号设置后不会再变,如果要更新类文件,就可以改变这个版本号,就会去校验它。
-forceprocessing 指定处理输入,即使输出已经是最新的了。更新的检测是基于对比指定的输入文件、输出文件、配置文件或目录的时间戳。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 压缩选项
-dontshrink 关闭压缩。默认开启,处理时会移除除用-keep保留(间接或直接依赖的都会被保留)的之外未被使用的类和类成员,并且会在优化阶段再次执行,因为优化后可能会再次暴露出一些未被使用的类和成员。
-printusage {filename} 将输入类文件中没有用的代码列出来,并打印到标准输出或给定的文件中
-whyareyoukeeping {class_specification} 给出为什么在压缩步骤要保留某些类和类成员的理由,当你想知道某个给定的元素为什么会在输出中时,就可以查看这个理由。这个选项只在压缩阶段有效。
- 1
- 2
- 3
- 保留选项
-keep {Modifier} {class_specification} 指定要保留的类文件和类的成员
-keepclassmembers {modifier} {class_specification} 指定要保留的类成员,如果此类也保留会更好
-keepclasseswithmembers {class_specification} 保留指定的类和类的成员,但条件是所有指定的类和类成员是要存在。
-keepnames {class_specification} 保留指定的类和类的成员的名称,如果他们在压缩步骤中,不会被删除。这是 -keep,allowshrinking class_specification的简写。例如你要保留所有实现了Serializable接口的类名。在混淆时起作用。
-keepclassmembernames {class_specification} 保留指定的类的成员的名称,如果他们不会被删除,在压缩步骤中。这是-keepclassmembers,allowshrinking class_specification的简写。在混淆时起作用。
-keepclasseswithmembernames {class_specification} 保留指定的类和类成员的名称,前提是在压缩步骤,这些类和类成员都还存在。这是-keepclasseswithmembers,allowshrinking class_specification的简写。
-printseeds {filename} 列出-keep选项的清单的类和类成员,将打印到标准输出或输出到给定的文件。这将会非常有用,可以用于验证已扩展的类成员是否能找到,尤其是用了通配符的情况下。如,可以例出所有你保留的,不用混淆的类和类成员。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 优化选项
-dontoptimize 关闭优化。默认开启,所有方法都会在字节码级别执行优化,让应用运行的更快。
-optimizationpasses n 指定优化次数。默认,一次。
-assumenosideeffects {class_specification} 指定没有副作用的方法(除了可能会返回值的)。在优化阶段,ProGuard会删除对这类方法的调用,如果返回值没有被使用。ProGuard会分析你的代码,会自动查找这一类的方法。ProGuard不会去分析库代码,正因如此,这个选项还是很有用的。
-allowaccessmodification 指定在处理期间可以扩展类和类成员的访问修饰符
-mergeinterfacesaggressively 可以合并接口,当它们的实现类没有实现所有接口方法时。这可以减少输出文件的大小从而减少类的总数。
- 1
- 2
- 3
- 4
- 5
- 混淆选项
-dontobfuscate 关闭混淆。默认开启,增大反编译难度,类和类成员会被随机命名,除非用keep保护。
-printmapping {filename} 指定输出类的新名称与旧名称和改名后的类成员。可以输出到标准输出或指定的文件。
-applymapping {filename} 重用-printmapping输出的映射,用于进行增量式混淆
-obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法能和字段的名称,默认情况下是使用像'a', 'b'等这样的短名称。
-classobfuscationdictionary filename 指定包含合法的用于混淆后的类的名称的字符集合的文本文件。
-packageobfuscationdictionary filename 通过文本文件指定合法的包名。
-overloadaggressively 混淆时允许多个字段和方法可以得到相同的名称,只要它们的参数和返回类型不同(不仅仅是它们的参数)。这可以让程序代码更小。
-useuniqueclassmembernames 分配相同的混淆名称给有相同名称的类成员。确定统一的混淆类的成员名称来增加混淆。
-flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
-repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
-repackageclasses [package_name] 重新包装所有已重命名的类文件,并将它们移动入给定的单一包中。
-dontusemixedcaseclassnames 混淆时不产生混合大小写的类名。默认情况下会。
-keeppackagenames [package_filter] 不混淆给定的包名
-keepattributes {attribute_name,...} 保留给定的可选属性,例如Exceptions, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, and AnnotationDefault,InnerClasses
-keepparameternames 保留参数名和方法类型。
-renamesourcefileattribute {string} 设置源文件中给定的字符串常量
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 预验证选项
-dontpreverify 关闭验证,默认是开启的。
-microedition 指定被处理的类文件对应的Java Micro Edition。
- 1
- 2
- 通用选项
-verbose 在处理期间,写出更多的的信息,如果遇到异常终止了,这个选项将打印出所有栈跟踪的信息。
-dontnote [class_filter] 不打印有关配置中潜在错误或遗漏的注释
-dontwarn [class_filter] 不警告未解决的引用和其他重要的问题
-ignorewarnings 打印有关未解决的引用和其他重要问题的任何警告
-printconfiguration [filename] 用包含的文件和替换的变量写出已分析的整个配置
-dump [filename] 写出类文件的内部结构
- 1
- 2
- 3
- 4
- 5
- 6
- 7
至此,Proguard.pro文件里可以的配置的东西都在上面有说明了,接下来我们看看如何用。
例子
压缩、优化、混淆的关闭
- 压缩(Shrinking):默认开启
-dontshrink #关闭压缩
- 1
- 优化(Optimization):默认开启
-dontoptimize #关闭优化
- 1
- 混淆(Obfuscation):默认开启
-dontobfuscate #关闭混淆
- 1
星号通配符:*代表当前包下的类,**代表当前包及子包下的类
- 保持某个类名不被混淆
-keep class com.wong.BaseDaoImpl
- 1
- 一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;
-keep class com.wong.demo.*
- 1
- 两颗星表示把本包和所含子包下的类名都保持;
-keep class com.wong.demo.**
- 1
注意:上面三种方式,类名虽然未混淆,但里面的类成员名称还是被混淆了
- 保持某个类的类名及内部的所有内容不会混淆
-keep class com.wong.BaseDaoImpl{*;}
- 1
- 既可以保持该包下的类名,又可以保持类里面的内容不被混淆;
-keep class com.wong.demo.*{*;}
- 1
- 既可以保持该包及子包下的类名,又可以保持类里面的内容不被混淆;
-keep class com.wong.demo.**{*;}
- 1
- 保持类中特定内容,而不是所有的内容可以使用如下:
-keep class com.wong.bean.MyBean{ <init>; #匹配所有构造器 <fields>;#匹配所有域 <methods>;#匹配所有方法
}
- 1
- 2
- 3
- 4
- 5
上面就保护了MyBean这个类中的所有的构造方法、变量、和方法不被混淆。
可以在<fields>或<methods>前面加上private 、public、native等来进一步指定不被混淆的内容,如:
-keep class com.wong.BaseDaoImpl{ public <methods>;#保护该类下所有的公有方法不被混淆 public *;#保护该类下所有的公有内容不被混淆 private <methods>;#保护该类下所有的私有方法不被混淆 private *;#保护该类下所有的私有内容不被混淆 public <init>(java.lang.String);#保护该类的有String类型参数的构造方法不被混淆
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 要保护一个类的内部类不被混淆用 $ 符号,如保护BaseDaoImpl类的内部MyInnerClass不被混淆
-keep class com.wong.BaseDaoImpl$MyInnerClass{*;}
- 1
- extends、implement:保护Android底层组件和类不要混淆
-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.view.View
- 1
- 2
- 3
- 4
- 5
- 6
- 不需要保持类名,只需保持该类下的特定方法保持不被混淆,如保持MyProguardTest类下test(String)方法不被混淆
-keepclassmembernames class com.wong.MyProguardTest{ public void test(java.lang.String);
}
- 1
- 2
- 3
- 保留类和类成员,类和类成员都不会被混淆
-keepclasseswithmembernames class com.wong.MyProguardTest1
- 1
重要注意事项
- jni方法不可混淆,因为native方法是用完整的包名类名方法名来定义的,不能修改,否则找不到
-keepclasseswithmembernames class * { native <methods>;
}
- 1
- 2
- 3
- AndroidMainfest中的类不混淆,所以四大组件和Application的子类和Framework层下所有的类默认不要进行混淆。
- 用GSON、FastJson等框架解析服务端数据时,所写的JSON对象类(实体类)不要混淆,否则无法将JSON解析成对应的对象。
- 使用第三方开源库或者引用其他第三方的SDK包时,如果有特别要求,也需要在混淆文件中加入对应的混淆规则
- WebView的JS调用Android写的接口需要保证接口方法不混淆,否则js找不到对应的方法调用
- 自定义的View默认也不会被混淆
- 反射用到的类混淆时需要注意:只保持反射用到的类名和方法即可,否则也会找不到,并不需要将整个被反射到的类都进行保持
- 使用enum时,不要混淆以下两个方法,它们会被反射调用到,如果混淆了,反射就会失效,找不到方法。
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
- 1
- 2
- 3
- 4
- Parcelable的子类和Creator静态成员变量不混淆,否则会产生Android.os.BadParcelableException异常;
-keep class * implements Android.os.Parcelable { public static final Android.os.Parcelable$Creator *;
}
- 1
- 2
- 3
Android Studio启用混淆
1、开启混淆
Android Studio自身集成Java语言的ProGuard作为压缩、优化、混淆的工具,配合Gradle构建工具使用。只需要在工程应用目录的gradle文件中设置minifyEnabled为true即可:
android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2、在proguard-rules.pro文件中加入混淆规则
# 代码混淆压缩比,在0~7之间,默认为5,一般不下需要修改
-optimizationpasses 5
# 混淆时不使用大小写混合,混淆后的类名为小写
# windows下的同学还是加入这个选项吧(windows大小写不敏感)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的库的类
# 默认跳过,有些情况下编写的代码与类库中的类在同一个包下,并且持有包中内容的引用,此时就需要加入此条声明
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers
# 不做预检验,preverify是proguard的四个步骤之一
# Android不需要preverify,去掉这一步可以加快混淆速度
-dontpreverify
# 有了verbose这句话,混淆后就会生成映射文件
# 包含有类名->混淆后类名的映射关系
# 然后使用printmapping指定映射文件的名称
-verbose
# 有了verbose这句话,混淆后就会生成映射文件
# 包含有类名->混淆后类名的映射关系
# 然后使用printmapping指定映射文件的名称
-verbose
#混淆前后的映射
-printmapping proguardMapping.txt
#apk 包内所有 class 的内部结构
-dump class_files.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从 apk 中删除的代码
-printusage unused.txt
# 指定混淆时采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不改变
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# 保护代码中的Annotation不被混淆
# 这在JSON实体映射时非常重要
-keepattributes *Annotation*
# 避免混淆泛型
# 这在JSON实体映射时非常重要
-keepattributes Signature
# Gson specific classes
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.google.gson.** { *;}
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * { native <methods>;
}
# 保留了继承自Activity、Application这些类的子类
# 因为这些子类有可能被外部调用
# 比如第一行就保证了所有Activity的子类不要被混淆
-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
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
#support.v4/v7包不混淆
-keep class android.support.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v7.** { *; }
-keep public class * extends android.support.v7.**
-keep interface android.support.v7.app.** { *; }
-dontwarn android.support.** # 忽略警告
# 保留Activity中的方法参数是view的方法,
# 从而我们在layout里面编写onClick就不会影响
-keepclassmembers class * extends android.app.Activity { public void * (android.view.View);
}
# 枚举类不能被混淆
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String);
}
# 保留自定义控件(继承自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); public void set*(***); *** get* ();
}
# 保留Parcelable序列化的类不能被混淆
-keep class * implements android.os.Parcelable{ public static final android.os.Parcelable$Creator *;
}
# 不混淆实体类
-keep class com.jxty.app.garden.model.** { *; }
# 保留Serializable 序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient <fields>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve();
}
# 对R文件下的所有类及其方法,都不能被混淆
-keepclassmembers class **.R$* { *;
}
# 对于带有回调函数onXXEvent的,不能混淆
-keepclassmembers class * { void *(**on*Event);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
执行ProGuard后会生成的文件:
1)dump.txt 描述apk文件里的所以类的内部结构
2)mapping.txt 列出了原始的和混淆后的类、方法和属性的对应关系
3)seeds.txt 列出了没有被混淆的类和属性
4)usage.txt 列出了没有被打到apk文件中的代码
路径:项目目录/module名称/build/outputs/mapping/release
也可以直接在Android studio中查看:
谢谢阅读
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/102606205
- 点赞
- 收藏
- 关注作者
评论(0)