Proguard是一个代码优化和混淆工具。
能够提供对Java类文件的压缩、优化、混淆,和预校验。压缩的步骤是检测并移除未使用的类、字段、方法和属性。优化的步骤是分析和优化方法的字节码。混淆的步骤是使用短的毫无意义的名称重命名剩余的类、字段和方法。压缩、优化、混淆使得代码更小,更高效。
所以proguard不只是用于混淆。
关于proguard的使用我就不提了,这里只总结常用语法
## ------------------------------------- 混淆基础配置 ---------------------------------------------
-optimizationpasses 5 # 指定代码的压缩级别
-dontusemixedcaseclassnames # 混淆时不会产生形形色色的类名
-dontskipnonpubliclibraryclasses # 指定不去忽略非公共的库类
-dontskipnonpubliclibraryclassmembers # 指定不去忽略包可见的库类的成员
-dontpreverify # 不预校验
-ignorewarnings # 屏蔽警告
-verbose # 混淆时记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #优化
-keepattributes *Annotation* # 保护代码中的Annotation不被混淆
-keepattributes Signature # 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes SourceFile,LineNumberTable # 抛出异常时保留代码行号
# 异常
-keepattributes Exceptions
#保留包裹类定义的直接方法
-keepattributes EnclosingMethod
//例如
public class Bug {
public static Object getBug(){
class ClassA{
};
return new ClassA();
}
}
//获取classA的直接包裹方法
{
Class> aClass = Bug.getBug().getClass();
Log.d("tedu", "onCreate: "+aClass.getEnclosingMethod());
}
//如果没有使用-keepattributes EnclosingMethod 就会返回null
-dontnote [class_filter]
不输出指定类的错误信息.
-dontwarn [class_filter]
不打印指定类的警告信息
-assumenosideeffects class_specification
可以指定移除哪些方法没有副作用,如在android开发中,如想在release版本可以把所有log输出都移除,可以配置:
# 在优化阶段移除相关方法的调用
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
keep和keepname的区别就是remove和name的区别了,具体看上图。
-keep 指定类和类成员(变量和方法)不被混淆。
-keep class com.dongnao.proxy.guard.test.Bug
(保护了类名)
-keep class com.dongnao.proxy.guard.test.Bug{
public static void *();
}
(保护了 public static void的没有参数的函数)
-keep class com.dev.demo.two.ClassTwoTwo {
public (int);
}
(保护了参数为int的构造函数)
-keep class com.dongnao.proxy.guard.test.Bug{
*;
}
(保护所有)
-keepclassmembers 指定类成员不被混淆(就是-keep的缩小版,不管类名了)。
-keepclassmembers class com.dongnao.proxy.guard.test.Bug
(这里没有写任何类成员,所以都被混淆了,相当于没用)
-keepclassmembers class com.dongnao.proxy.guard.test.Bug {
** mChildView;
}
(保护某个名为mChildView的成员变量)
-keepclassmembers class com.dongnao.proxy.guard.test.Bug {
private ;
}
(保护所有私有成员变量)
-keepclasseswithmembers 指定类和类成员不被混淆,前提是指定的类成员存在。
-keepclasseswithmembers class com.dongnao.proxy.guard.test.Bug
(保护类名,但是没指定成员,所以函数名被混淆)
-keepclasseswithmembers class com.dongnao.proxy.guard.test.Bug{
native ;
}
全被混淆了。注意 前提是指定的类成员存在 如果不存在native函数,所以这条语句等于无效,
混淆移除过滤具体可以参考这篇文章https://mp.weixin.qq.com/s/DE4gr8cTRQp2jQq3c6wGHQ
参考http://ju.outofmemory.cn/entry/233480
表达式中的 class 关键字表示任何接口类、抽象类和普通类; interface 关键字表示只能是接口类; enum 关键字表示只能是枚举类。如果在 interface 和 enum 关键字前面加上感叹号(“ ! ”)分别表示不是接口类的类和不是枚举类的类。 class 关键字前面是不能加感叹号的。
对于类名( classname )来说,可以是类全名,或者可以包含以下一些特殊字符的正则表达式:
1) ? :问好代表一个任意字符,但不能是句号(“ . ”,因为句号是包名分隔符);
2) * :单个星号代表任意个任意字符,但不能代表句号;
3) ** :两个星号代表任意个任意字符,且能代表句号。
对于单个星号来说,如果类名部分只有一个星号,不包含其它任何字符,为了保证兼容性,其代表任何类,就跟两个星号的作用一样了。
extends 和 implements 表示限定类一定要扩展自一个指定类或者实现了一个指定接口类,这时候通常类名部分是一个星号。
对于类中的成员变量(Fields)来说,可以通过变量类型 fieldtype 和变量名 fieldname 来精确指定,也可以通过 表示类中的任何成员变量。
对于类中的成员函数(Methods)来说,可以通过返回类型 returntype 、方法名 methodname 和参数类型 argumenttype 来唯一限定,也可以通过 来表示类中的任何成员函数。
对于类的构造函数来说,可以用 加上构造函数的参数来指定。
星号(“ * ”)可以匹配类中的任何成员变量和函数。
对于类中的成员函数名 methodname 和成员变量名 fieldname 来说也可以使用通配符来匹配,同样问号(“ ? ”)可以匹配一个任意字符,而星号(“ * ”)可以匹配任意多个任意字符。
对于类中的成员变量的类型、成员函数的返回类型和参数类型,以及构造函数的参数类型来说,可以使用下面这些通配符来匹配:
1) % :匹配任何原始类型,如 boolean 、 int 等,但不包括 void ;
2) ? :匹配一个任意字符,不包括句号;
3) * :匹配任意个任意字符,不包括句号;
4) ** :匹配任意个任意字符,包括句号;
5) * :匹配任意类型,包括原始类型和非原始类型,数组类型和非数组类型;
6) … :匹配任何数目个任何类型的参数。
在类名前、类中成员变量和成员函数名前,可以加上访问限定符(如 public 、 private 、 protected 等,修饰类、成员变量和成员函数的访问限定符各不相同)。如果加上了访问限定符后,就表示要匹配的类、成员变量或成员函数的定义中必须包含这些限定符。如果在限定符前面加上感叹号“ ! ”,则刚好相反,定义中必须不包含这些限定符
$ 在类名后表示内部类
匹配了构造方法;
匹配了成员变量;
匹配了方法;
关于glide中使用的混淆保留了内部枚举类
例如:
-keep public enum com.stuff.MyConfigObject$** {
**[] $VALUES;
public *;
}
这样将保留所有MyConfigObject的内部枚举,至于**[] $VALUES,枚举被编译后编程类似于这样
public enum Stuff extends Enum {
/*public static final*/ COW /* = new Stuff("COW", 0) */,
/*public static final*/ POTATO /* = new Stuff("POTATO", 1) */,
/*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */;
/*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE};
public static Stuff[] values() {
return (Stuff[])$VALUES.clone();
}
public static Stuff valueOf(String name) {
return (Stuff)Enum.valueOf(Stuff.class, name);
}
private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
}
在Android studio中
开启混淆后有一个默认文件和一个指定的文件
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
默认文件的需要首先执行gradle命令extractProguardFiles,然后会在跟目录下的build/intermediates/proguard-files中查看默认的proguard文件。例如build/intermediates/proguard-files/proguard-android.txt-3.0.1,该文件指定了一些常用的规则,而最终混淆是通过两个文件综合规则来进行混淆的。
语法通用格式
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
[extends|implements [@annotationtype] classname]
[{
[@annotationtype] [[!]public|private|protected|static|volatile|transient ...] |
(fieldtype fieldname);
[@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] |
(argumenttype,...) |
classname(argumenttype,...) |
(returntype methodname(argumenttype,...));
[@annotationtype] [[!]public|private|protected|static ... ] *;
...
}]
常用的大概就这些了,其他不常用的开发基本用不到,如果有什么不对的或者补充的,欢迎评论补充,一起学习进步。