一、ProGuard简介
ProGuard用于将java 的class file进行压缩、优化、混淆、预校验。
下面是ProGuard的处理流程图
第一步、压缩
检测并移除无用的类、成员变量、方法、属性。
第二步、优化
分析和优化method的字节码
第三步、混淆
使用无意义的短名称命名类、字段、方法等,混淆后名称变为a,b等
第四步、预校验
给classes添加预校验信息(which is required for Java Micro Edition and for Java 6 and higher)。
注意:以上步骤都是可选的,可进行配置
二、语法
Input/Output Options
-include {filename}
从给定的文件中读取配置参数
-basedirectory {directoryname}
指定基础目录为以后相对的档案名称
-injars {class_path}
指定要处理的应用程序jar,war,ear和目录
-outjars {class_path}
指定处理完后要输出的jar,war,ear和目录的名称
-libraryjars {classpath}
指定要处理的应用程序jar,war,ear和目录所需要的程序库文件
-dontskipnonpubliclibraryclasses
指定不去忽略非公共的库类。
-dontskipnonpubliclibraryclassmembers
指定不去忽略包可见的库类的成员。
保留选项
-keep {Modifier} {class_specification}
保护指定的类文件和类的成员
-keepclassmembers {modifier} {class_specification}
保护指定类的成员,如果此类受到保护他们会保护的更好
-keepclasseswithmembers {class_specification}
保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。
-keepnames {class_specification}
保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)
-keepclassmembernames {class_specification}
保护指定的类的成员的名称(如果他们不会压缩步骤中删除)
-keepclasseswithmembernames {class_specification}
保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)
-printseeds {filename}
列出类和类的成员-keep选项的清单,标准输出到给定的文件
压缩
-dontshrink
不压缩输入的类文件
-printusage {filename}
-whyareyoukeeping {class_specification}
优化
-dontoptimize
不优化输入的类文件
-assumenosideeffects {class_specification}
优化时假设指定的方法,没有任何副作用
-allowaccessmodification
优化时允许访问并修改有修饰符的类和类的成员
混淆
-dontobfuscate
不混淆输入的类文件
-printmapping {filename}
-applymapping {filename}
重用映射增加混淆
-obfuscationdictionary {filename}
使用给定文件中的关键字作为要混淆方法的名称
-overloadaggressively
混淆时应用侵入式重载
-useuniqueclassmembernames
确定统一的混淆类的成员名称来增加混淆
-flattenpackagehierarchy {package_name}
重新包装所有重命名的包并放在给定的单一包中
-repackageclass {package_name}
重新包装所有重命名的类文件中放在给定的单一包中
-dontusemixedcaseclassnames
混淆时不会产生形形色色的类名
-keepattributes {attribute_name,...}
保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-renamesourcefileattribute {string}
设置源文件中给定的字符串常量
三、类的规范
1、关键字
class:表示任何class或interface
interface:只能是接口
enum :枚举
!:表示非,比如!interface 表示非接口
2、类名规则:
可以指定完整类名,比如java.lang.String,内部类使用$符号,比如java.lang.Thread$State。类名也可以使用正则表达式:
?
matches any single character in a class name, but not the package separator.
比如 "mypackage.Test?" matches "mypackage.Test1" and "mypackage.Test2", but not "mypackage.Test12".
*
matches any part of a class name not containing the package separator.
比如 "mypackage.*Test*" matches "mypackage.Test" and "mypackage.YourTestApplication", but not "mypackage.mysubpackage.MyTest". Or, more generally, "mypackage.*" matches all classes in "mypackage", but not in its subpackages.
**
matches any part of a class name, possibly containing any number of package separators.
比如 "**.Test" matches all Test classes in all packages except the root package. Or, "mypackage.**" matches all classes in "mypackage" and in its subpackages.
3、extends、implements
继承某个类、或实现某个接口
4、 @
用于指定使用了某个注解,比如
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
5、Fields and methods
<init> 匹配任何构造方法.
<fields> 匹配任何field.
<methods> 匹配任何method.
* 匹配任何field或method
比如
-keep class com.umeng.message.* {
public <fields>;
public <methods>;
}
-keepclassmembers class * {
public <init>(org.json.JSONObject);
}
Fields and methods 也可以使用正则表达式,字段名或方法名可以包含:
?
matches any single character in a method name.
*
matches any part of a method name.
其他类型可以包含如下通配符
% matches any primitive type ("boolean", "int", etc, but not "void").
? matches any single character in a class name.
* matches any part of a class name not containing the package separator.
** matches any part of a class name, possibly containing any number of package separators.
*** matches any type (primitive or non-primitive, array or non-array).
... matches any number of arguments of any type.
注意:?, *, and ** 不能匹配原始数据类型, 只有***能匹配数组类型(包括多维数组),比如
"** get*()" matches "java.lang.Object getObject()", but not "float getFloat()", nor "java.lang.Object[] getObjects()".
6、权限修饰符
用来严格匹配,修饰符可以指定多个,比如public static。
ProGuard 还支持 synthetic, bridge, and varargs 修饰符。
四、文件
mapping.txt
表示混淆前后代码的对照表,这个文件非常重要。如果你的代码混淆后会产生bug的话,log提示中是混淆后的代码,希望定位到源代码的话就可以根据mapping.txt反推。
每次发布都要保留它方便该版本出现问题时调出日志进行排查,它可以根据版本号或是发布时间命名来保存或是放进代码版本控制中。
dump.txt
描述apk内所有class文件的内部结构。
seeds.txt
列出了没有被混淆的类和成员。
usage.txt
列出了源代码中被删除在apk中不存在的代码。
五、混淆原则
Android默认添加的混淆
1、定义在AndroidManifest.xml中所有类
比如Activity、Service、Receiver、Provider
2、注解Annotations
3、自定义View不能混淆,因为它们可能在xml中被引用
4、onClick(View v)
因为我们可以在xml中指定onClick方法
5、Parcelable中的静态字段
6、R中的内部类及静态字段
7、WebView使用@JavaInterface标注的方法
8、如果使用Google APIs,还需要保留
-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar
-keep public interface com.android.vending.licensing.ILicensingService
9、support兼容库
-dontwarn android.support.**
10、 native methods, callback methods, enumerations, and resource files.
开发中常用的混淆规则
1、反射时用到了字符串匹配的不能混淆
比如Gson根据json里面的key反射class中的字段。如果class被混淆了,则json中的key就无法匹配了。
2、注解
比如EventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
需要指定使用了指定注解的方法不能被混淆。
参考
官网 https://www.guardsquare.com/en/proguard/manual/introduction