对于一个有经验的程序员,ProGuard大家一定不陌生,有人说他是混淆,我必须纠正,混淆只是ProGuard的一个过程(我不会称ProGuard为混淆,ProGuard就是ProGuard,谁再说ProGuard才是真正的混淆视听,下面有解释).
ProGuard其实不是Android特有的工具,他其实更早适用于java项目中,优化java代码,保证java程序的安全性。由于Android程序大部分也是是Java代码,所以ProGuard成为Android工程师必修的一门课程。

简单认识ProGuard

在平时开发中,我们的项目是ProGuard开关默认是是关的,我们通过如下代码打开ProGuard:

1
2
3
4
5
6
7
8
9
10
11
android {

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

}
}

}

先解释下我们可以通过minifyEnabled打开开关,通过proguardFiles指定我们的ProGuard Rule的多个文件(后面会介绍)。我们也可以使用proguardFile指定一个一个文件,其中proguard-android.txt实际上是我们的{Android Sdk path}/tools/proguard/proguard-android.txt。这样就可以简单使用我们的ProGuard,当然了对应的ProGuard也是我们SDK下proguard-android.txt

什么是ProGuard

那个究竟什么是ProGuard呢,我们可以看下{Android Sdk path}/tools/proguard/下的ProGuard的doc文档:

ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names. Finally, it preverifies the processed code for Java 6 or for Java Micro Edition.

大概意思就是: ProGuard是一个免费的可以将java class文件进行压缩,优化,混淆,预校验的工具。它能删除无用的类,变量、方法以及属性。它能优化字节码,移除无用的指令,同时它将(压缩后)保留的类,变量和方法使用无意义的段字符来重命名,而后还能在对应的java版本上进行预校验这些处理后的code。

## ProGuard四兄弟

如上图,ProGuard实际上四大天王(魔家四将),他们分别是压缩(shrinking),优化(optimization),混淆(obfuscation)和预校验(preverification),一个class和四大天王碰过面才可以到最后的南天门(。

压缩(shrinking):检测并移除代码中无用的类、字段、方法和特性(Attribute)
优化(optimization):对字节码进行优化,移除无用的指令
混淆(obfuscation):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名
预检(preverification):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。
对于一个class文件也要经历如下四步:

  1. 会首先检查这个类是否有地方用到(没用到直接删除),这个类里面的方法或者变量哪些我们没用到,没用到就直接删除

  2. 若类没有被删除,检查class文件的无用指令和字节

  3. 将仍然保留的类,混淆class名及内部的变量和方法名

  4. 在对应的java的平台上预校验这个class是否完整有效
    这个是默认的情况下经历着四个步骤,这个也不是必须的。很像目前中国的大环境,你可以通过人情来避免不必要的麻烦,这四大天王也不例外。
    例如我写个SDK,里面封装很多java文件,最后类库为第三方服务,最后我打出一个jar,这个jar我期望用混淆。严格按照混淆的四大天王的规则,就会遇到很多问题:

  5. 保留一些类。里面的类好对外使用的,不会被SDK内部使用,按照混淆的四大天王的规则,第一步直接就被干掉了,所有就得走点后门
    2. 某些class,method或者变量,我期望不要混淆。例如一个支付的class为PaySDK,里面的方法也尽量是aliPay,wxPay,名称有意义,使用混淆后PaySDK成了a,方法名也是无意义的a,b,这对使用sdk的开发者是不友好的。
    为了避免这个问题,就需要我们自己来制定一个规则,在去南天门的路上,你只管这个规则来给四大天王看,告诉按照我们的规则走,而这个规则就是ProGuard Rule我们刚才配置的proguardFiles,对于我们来说写一个ProGuard Rule是目前比较不太好写的一件事。

ProGuard Rule

在第一部分我们提到在主项目下的build.gradle配置我们的ProGuard Rule,其中Google官方提供了一个简单的基础示例
我们就来大致看下他们是分别的作用:

1
2
3
# dont 一般是代表do not不要的意思,这个就表示混淆的字符不适用大小写混合的(如Ab)
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses

这个是Google建议的,若你非使用大小混合,没人会拦你。😆。


1
2
3
# 不优化,dex不是运行在JVM上的,当然了也不做预校验
-dontoptimize
-dontpreverify

这个是针对android的开发而言,不过你要使用优化,尽量使用{Android Sdk path}/tools/proguard/下的proguard-android-optimize.txt文件。


1
2
3
4
5
# 注解相关的
-keepattributes *Annotation*
# 猜测是Google服务相关的
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

可以看到有个keepattributes肯定是保留类的属性啦,还有个-keep public class 类名这个肯定就是为了保留这个类不被压缩或者混淆。


1
2
3
-keepclasseswithmembernames class * {
native <methods>;
}

这个-keepclasseswithmembernames public class 类名不难解释吧,字面意思是保留class和类的成员,并且这个类有个native方法。


1
2
3
4
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}

这个就代表保留类中的getter/setter方法,这个类是继承自android.view.View


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

这个肯定是保留我们资源ID对应的R文件中所有的静态变量。


看了这么多我们会产生疑问:keep、keepclassmembers等这些keep开头的有哪些,区别是什么,怎么配置参数,class,method这些怎么写,*以及分别代表什么,就让我们来一起探索ProGuard Rule的写法吧。

Keep options

keep目前常见的就6六种:

  1. -keep [,modifier,…] class_specification
  2. -keepclassmembers [,modifier,…] class_specification
  3. -keepclasseswithmembers [,modifier,…] class_specification
  4. -keepnames class_specification 在压缩(删除无用的类和成员)后保留下来的类和其成员中,阻止类和类的成员(里面的变量和方法都是成员)混淆
  5. -keepclassmembernames class_specification 在压缩后保留下来的类中,阻止类的成员被混淆
  6. -keepclasseswithmembernames class_specification

有无name的keep*

可以看到前三个实际上是比后三个少了name的,但是意义却大不同啊。就以keep和keepName为例吧,
keep是阻止类和类的变量(实际上就是类中的方法和变量)被压缩和混淆,而keepnames那么则是在压缩后,防止类和类的成员被混淆。换句话说,keep在压缩第一道大门时,就已经生效,可以避免被压缩掉,即使这个类或者成员没有被用到也不会被移除,而keepnames则是在压缩后,从保留的类中来看哪些类和成员不能被混淆,是在压缩后才生效。
官方文档解释的很到位:

Short for -keep,allowshrinking class_specification

实际上加了allowshrinking的kepp命令,允许压缩。

关键字 概述
keep 阻止类和成员被和混淆
keepclassmember 阻止类的成员被和混淆
keepclasseswithmembers 阻止类和类中特定的成员被压缩和混淆
keepnames 在压缩后保留下来的类和其成员中,阻止类和类的成员(里面的变量和方法都是成员)混淆
keepclassmembernames 在压缩后保留下来的类和其成员中,阻止类的成员被混淆
keepclassmembernames 在压缩后保留下来的类和其成员中,阻止类和类中特定的成员被混淆

keep VS keepclasseswithmembers

这两个都可以注释保留住class及其成员,但是二者区分最大之处在于:keep只看后面的class 是什么,不关心后面的成员,而keepclasseswithmembers则是更关心后面的条件。干说太硬,来点湿的。
如下规则:

1
2
3
-keepclasseswithmembernames class me.cyning.* {
native <methods>;
}

当在me.cyning包下,遇到方法体有native方法时,则keep其class,同时keep这个class的native的方法(其他无用方法可能会被删掉或者方法名会被混淆)。

1
2
3
-keep class me.cyning.* {
native <methods>;
}

则会将class不压缩

这个keep*可以保证类或者方法不被压缩和混淆,至于选哪个看你心情喽。

##

Class specifications

在刚才示例中,有个代码片段:

1
-keep public class com.google.vending.licensing.ILicensingService

通过这一句keepcom.google.vending.licensing.ILicensingService,但是我们若是keep某一类时而不只是一个class时,我们要怎么办?
这就需要我们将这一类的做个抽象,而将这所有的类都列出来一个个keep。

我们可以来看下class的基本示例:

1
2
3
4
5
6
7
8
9
10
11
12
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
[extends|implements [@annotationtype] classname]
[{
[@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
(fieldtype fieldname);
[@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
<init>(argumenttype,...) |
classname(argumenttype,...) |
(returntype methodname(argumenttype,...));
[@annotationtype] [[!]public|private|protected|static ... ] *;
...
}]

先来解释下[]吧,这个都是可选的,可以选择无,也可以选择[]里的一种或者多种。如:

1
2
3
@me.cyning.Keep class * {
native <methods>()
}

就表示所有加了me.cyning.Keep类注解且有native方法的类。
就让我们分别来看Class specifications

  1. annotationtype实际上就是类注解,如上面@me.cyning.Keep
  2. class/interface/enum 前可以添加修饰符如public,final,abstract等修饰符,也可以在这些修饰符前加!表示否定,如!private 非私有的;在class/interface/enum前也可以加!表示否定,都是黑科技啊
  3. extends/implements 后可以添加我们继承或者实现的类或者接口,但是记住一定要写全名(包名+classname)。
  4. 表示里面的变量,他的前面可以加入修饰符publicprivateprotectedstaticvolatiletransient等。
  5. 可以表示出了构造函数外的方法,它前面也可以加入publicprivateprotectedstatic|synchronizednativeabstractstrictfp等修饰符;而则表示构造函数
  6. 针对一个class/interface/enum下的所有方法我们可以用*来表示如 public *;则表示所有修饰符为public的方法和变量。

针对上面的问题,我们还遗漏了一个很重的东西就是通配符,如,*。而通配符又分为以下:

packageName/className/methodName

包名或者类名是我们常见需要用到的,我们列出我们常用的通配符。
通配符 | 概述
—- | —
? | 表示任何一个任意但不是包名中的.分隔符。如me.cyning.Test?可以表示me.cyning.Test1me.cyning.TestA,而不能代表me.cyning.Test11

  • | 表示任何多个字符但不能是包名中的.分隔符。如me.cyning.Test*可以表示me.cyning.Test1me.cyning.Test11,而不能代表me.cyning.Test.A
    • | 表示任何多个字符,可以是包名中的.分隔符,如me.cyning.**可以表示me.cyning.Test1me.cyning.Test11,也可以是me.cyning.Test.A

类型描述

包名或者类名是我们常见需要用到的,我们列出我们常用的通配符。和packageName/className/methodName有类似之处
通配符 | 概述
—- | —
% | 除了void的基本类型
? | 单个字符

  • | 不包括.包分隔符的任意类名的一部分
    • | 包括.包分隔符的任意类名的一部分
  • ** | 任意类型
    … | 可表示多个任意且任意类型的类型的参数 如init(…)可代表init(int a, int b) 也可以代表是init(String)
    熟悉了ProGuard Rule的规则,那么我们再来看最开始我们提到的[默认的ProGuard Rule](—
    title: android_better_progurad
    date: 2017-10-30 22:08:30
    tags:

过了19大,党有了未来5年的规划,新理念你Get了没?(我觉得开完19大,做地铁都轻松多了,哈哈)。我也给自己裂了个规划,两周一篇博客,越干(gan 一声)越好。今天给自己带来的干货是ProGuard。
对于一个有经验的程序员,ProGuard大家一定不陌生,有人说他是混淆,我必须纠正,混淆只是ProGuard的一个过程(我不会称ProGuard为混淆,ProGuard就是ProGuard,谁再说ProGuard才是真正的混淆视听).
ProGuard其实不是Android特有的工具,他其实更早适用于java项目中,优化java代码,保证java程序的安全性。由于Android程序大部分也是是Java代码,所以ProGuard成为Android工程师必修的一门课程。

简单认识ProGuard

在平时开发中,我们的项目是ProGuard开关默认是是关的,我们通过如下代码打开ProGuard:

1
2
3
4
5
6
7
8
9
10
11
android {

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

}
}

}

先解释下我们可以通过minifyEnabled打开开关,通过proguardFiles指定我们的ProGuard Rule的多个文件(后面会介绍)。我们也可以使用proguardFile指定一个一个文件,其中proguard-android.txt实际上是我们的{Android Sdk path}/tools/proguard/proguard-android.txt。这样就可以简单使用我们的ProGuard,当然了对应的ProGuard也是我们SDK下proguard-android.txt

什么是ProGuard

那个究竟什么是ProGuard呢,我们可以看下{Android Sdk path}/tools/proguard/下的ProGuard的doc文档:

ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names. Finally, it preverifies the processed code for Java 6 or for Java Micro Edition.

大概意思就是: ProGuard是一个免费的可以将java class文件进行压缩,优化,混淆,预校验的工具。它能删除无用的类,变量、方法以及属性。它能优化字节码,移除无用的指令,同时它将(压缩后)保留的类,变量和方法使用无意义的段字符来重命名,而后还能在对应的java版本上进行预校验这些处理后的code。
## ProGuard四兄弟

如上图,ProGuard实际上四大天王(魔家四将),他们分别是压缩(shrinking),优化(optimization),混淆(obfuscation)和预校验(preverification),一个class和四大天王碰过面才可以到最后的南天门(。

压缩(shrinking):检测并移除代码中无用的类、字段、方法和特性(Attribute)
优化(optimization):对字节码进行优化,移除无用的指令
混淆(obfuscation):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名
预检(preverification):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。
对于一个class文件也要经历如下四步:

  1. 会首先检查这个类是否有地方用到(没用到直接删除),这个类里面的方法或者变量哪些我们没用到,没用到就直接删除

  2. 若类没有被删除,检查class文件的无用指令和字节

  3. 将仍然保留的类,混淆class名及内部的变量和方法名

  4. 在对应的java的平台上预校验这个class是否完整有效
    这个是默认的情况下经历着四个步骤,这个也不是必须的。很像目前中国的大环境,你可以通过人情来避免不必要的麻烦,这四大天王也不例外。
    例如我写个SDK,里面封装很多java文件,最后类库为第三方服务,最后我打出一个jar,这个jar我期望用混淆。严格按照混淆的四大天王的规则,就会遇到很多问题:

  5. 保留一些类。里面的类好对外使用的,不会被SDK内部使用,按照混淆的四大天王的规则,第一步直接就被干掉了,所有就得走点后门
    2. 某些class,method或者变量,我期望不要混淆。例如一个支付的class为PaySDK,里面的方法也尽量是aliPay,wxPay,名称有意义,使用混淆后PaySDK成了a,方法名也是无意义的a,b,这对使用sdk的开发者是不友好的。
    为了避免这个问题,就需要我们自己来制定一个规则,在去南天门的路上,你只管这个规则来给四大天王看,告诉按照我们的规则走,而这个规则就是ProGuard Rule我们刚才配置的proguardFiles,对于我们来说写一个ProGuard Rule是目前比较不太好写的一件事。

ProGuard Rule

在第一部分我们提到在主项目下的build.gradle配置我们的ProGuard Rule,其中Google官方提供了一个简单的基础示例
我们就来大致看下他们是分别的作用:

1
2
3
# dont 一般是代表do not不要的意思,这个就表示混淆的字符不适用大小写混合的(如Ab)
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses

这个是Google建议的,若你非使用大小混合,没人会拦你。😆。


1
2
3
# 不优化,dex不是运行在JVM上的,当然了也不做预校验
-dontoptimize
-dontpreverify

这个是针对android的开发而言,不过你要使用优化,尽量使用{Android Sdk path}/tools/proguard/下的proguard-android-optimize.txt文件。


1
2
3
4
5
# 注解相关的
-keepattributes *Annotation*
# 猜测是Google服务相关的
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

可以看到有个keepattributes肯定是保留类的属性啦,还有个-keep public class 类名这个肯定就是为了保留这个类不被压缩或者混淆。


1
2
3
-keepclasseswithmembernames class * {
native <methods>;
}

这个-keepclasseswithmembernames public class 类名不难解释吧,字面意思是保留class和类的成员,并且这个类有个native方法。


1
2
3
4
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}

这个就代表保留类中的getter/setter方法,这个类是继承自android.view.View


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

这个肯定是保留我们资源ID对应的R文件中所有的静态变量。


看了这么多我们会产生疑问:keep、keepclassmembers等这些keep开头的有哪些,区别是什么,怎么配置参数,class,method这些怎么写,*以及分别代表什么,就让我们来一起探索ProGuard Rule的写法吧。

Keep options

keep目前常见的就6六种:

  1. -keep [,modifier,…] class_specification
  2. -keepclassmembers [,modifier,…] class_specification
  3. -keepclasseswithmembers [,modifier,…] class_specification
  4. -keepnames class_specification 在压缩(删除无用的类和成员)后保留下来的类和其成员中,阻止类和类的成员(里面的变量和方法都是成员)混淆
  5. -keepclassmembernames class_specification 在压缩后保留下来的类中,阻止类的成员被混淆
  6. -keepclasseswithmembernames class_specification

有无name的keep*

可以看到前三个实际上是比后三个少了name的,但是意义却大不同啊。就以keep和keepName为例吧,
keep是阻止类和类的变量(实际上就是类中的方法和变量)被压缩和混淆,而keepnames那么则是在压缩后,防止类和类的成员被混淆。换句话说,keep在压缩第一道大门时,就已经生效,可以避免被压缩掉,即使这个类或者成员没有被用到也不会被移除,而keepnames则是在压缩后,从保留的类中来看哪些类和成员不能被混淆,是在压缩后才生效。
官方文档解释的很到位:

Short for -keep,allowshrinking class_specification

实际上加了allowshrinking的kepp命令,允许压缩。

关键字 概述
keep 阻止类和成员被和混淆
keepclassmember 阻止类的成员被和混淆
keepclasseswithmembers 阻止类和类中特定的成员被压缩和混淆
keepnames 在压缩后保留下来的类和其成员中,阻止类和类的成员(里面的变量和方法都是成员)混淆
keepclassmembernames 在压缩后保留下来的类和其成员中,阻止类的成员被混淆
keepclassmembernames 在压缩后保留下来的类和其成员中,阻止类和类中特定的成员被混淆

keep VS keepclasseswithmembers

这两个都可以注释保留住class及其成员,但是二者区分最大之处在于:keep只看后面的class 是什么,不关心后面的成员,而keepclasseswithmembers则是更关心后面的条件。干说太硬,来点湿的。
如下规则:

1
2
3
-keepclasseswithmembernames class me.cyning.* {
native <methods>;
}

当在me.cyning包下,遇到方法体有native方法时,则keep其class,同时keep这个class的native的方法(其他无用方法可能会被删掉或者方法名会被混淆)。

1
2
3
-keep class me.cyning.* {
native <methods>;
}

则会将class不压缩

这个keep*可以保证类或者方法不被压缩和混淆,至于选哪个看你心情喽。

##

Class specifications

在刚才示例中,有个代码片段:

1
-keep public class com.google.vending.licensing.ILicensingService

通过这一句keepcom.google.vending.licensing.ILicensingService,但是我们若是keep某一类时而不只是一个class时,我们要怎么办?
这就需要我们将这一类的做个抽象,而将这所有的类都列出来一个个keep。

我们可以来看下class的基本示例:

1
2
3
4
5
6
7
8
9
10
11
12
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
[extends|implements [@annotationtype] classname]
[{
[@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
(fieldtype fieldname);
[@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
<init>(argumenttype,...) |
classname(argumenttype,...) |
(returntype methodname(argumenttype,...));
[@annotationtype] [[!]public|private|protected|static ... ] *;
...
}]

先来解释下[]吧,这个都是可选的,可以选择无,也可以选择[]里的一种或者多种。如:

1
2
3
@me.cyning.Keep class * {
native <methods>()
}

就表示所有加了me.cyning.Keep类注解且有native方法的类。
就让我们分别来看Class specifications

  1. annotationtype实际上就是类注解,如上面@me.cyning.Keep
  2. class/interface/enum 前可以添加修饰符如public,final,abstract等修饰符,也可以在这些修饰符前加!表示否定,如!private 非私有的;在class/interface/enum前也可以加!表示否定,都是黑科技啊
  3. extends/implements 后可以添加我们继承或者实现的类或者接口,但是记住一定要写全名(包名+classname)。
  4. 表示里面的变量,他的前面可以加入修饰符publicprivateprotectedstaticvolatiletransient等。
  5. 可以表示出了构造函数外的方法,它前面也可以加入publicprivateprotectedstatic|synchronizednativeabstractstrictfp等修饰符;而则表示构造函数
  6. 针对一个class/interface/enum下的所有方法我们可以用*来表示如 public *;则表示所有修饰符为public的方法和变量。

针对上面的问题,我们还遗漏了一个很重的东西就是通配符,如,*。而通配符又分为以下:

packageName/className/methodName

包名或者类名是我们常见需要用到的,我们列出我们常用的通配符。
通配符 | 概述
—- | —
? | 表示任何一个任意但不是包名中的.分隔符。如me.cyning.Test?可以表示me.cyning.Test1me.cyning.TestA,而不能代表me.cyning.Test11

  • | 表示任何多个字符但不能是包名中的.分隔符。如me.cyning.Test*可以表示me.cyning.Test1me.cyning.Test11,而不能代表me.cyning.Test.A
    • | 表示任何多个字符,可以是包名中的.分隔符,如me.cyning.**可以表示me.cyning.Test1me.cyning.Test11,也可以是me.cyning.Test.A

类型描述

包名或者类名是我们常见需要用到的,我们列出我们常用的通配符。和packageName/className/methodName有类似之处
通配符 | 概述
—- | —
% | 除了void的基本类型
? | 单个字符

  • | 不包括.包分隔符的任意类名的一部分
    • | 包括.包分隔符的任意类名的一部分
  • ** | 任意类型
    … | 可表示多个任意且任意类型的类型的参数 如init(…)可代表init(int a, int b) 也可以代表是init(String)
    熟悉了ProGuard Rule的规则,那么我们再来看最开始我们提到的默认的ProGuard Rule

—— 建议读者有时间结合上面介绍的Class specifications和Keep Options,来重新认识和验证下。——–

自定义ProGuard Rule)

我们要自定义的ProGuard Rule,其实也是三步走:基本Proguard Rule,业务中常见代码的ProGuard Rule,第三方SDK的ProGuard Rule,一般情况主要按照三步走,尽量做到ProGuard涉及到我们应ProGuard之处。

基本Proguard Rule

可以直接参考我们的默认的ProGuard Rule,这个一般不会涉及到业务,也可以在其尾部继续适当加入,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 避免混淆泛型,这在JSON实体映射时非常重要,比如fastJson
-keepattributes Signature

# 抛出异常时保留代码行号,在异常分析中可以方便定位
-keepattributes SourceFile,LineNumberTable

# 抛出异常时保留代码行号,在异常分析中可以方便定位
-keepattributes SourceFile,LineNumberTable

# 保留Serializable序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

# 对于R(资源)下的所有类及其方法,都不能被混淆
-keep class **.R$* {
*;
}

这个大家是不是基本上是我们最最常见的,不是一定如此,这个是个人观点,仅供参考使用。

业务常见

这个需要结合自己的业务来做,或者来看,例如我的:

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
#
保留实体类和成员不被混淆
-keep public class com.xxxx.entity.** {
public void set*(***);
public *** get*();
public *** is*();
}



# 对WebView的处理
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String)
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, java.lang.String)
}
# 保留JS方法不被混淆
-keepclassmembers class com.example.xxx.MainActivity$JSInterface1 {
<methods>;
}

# 反射中用到的类或者方法


### 第三方库
一个优秀的开源库,一般会提供我们ProGuard Rule,都说啦是一班啦,二班的同学还是需要根据源代码来自己为开源库添加ProGuard Rule。

```java
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

-dontwarn com.alipay.android.app.**
-keep public class com.alipay.** { *; }

网上有雷锋帮我们准备了一份:msdx/android-proguard-cn,拿走不谢。

彩蛋

之前看赵四大哥的一篇文章,Android安全防护之旅—带你把Apk混淆成中文语言代码 ,它从源码上做了处理,不过这个不是我期望的,后来居然发现,ProGuard居然真的可以设置mappding的字典:
-obfuscationdictionary naruto.txt
naruto已经上传到gitgist啦.
打开有惊喜哦。