这篇文章上次修改于 2169 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
本文已授权微信公众号「玉刚说」独家发布。

欢迎大家来看「Java 混淆那些事」的第四篇,到目前我们现在已经可以进行编写混淆规则了,但是有个很严重问题,我们写的规则会出现很多重复的 keep 规则,我们随便举几个例子。

  1. 所有 Event 结尾的类里面的所有内容都不能混淆。
  2. 所有 XXX 子类中的 x() 方法都不混淆。
    ...

那么这些问题有没有什么办法来解决呢?有,就是今天我们要讲的过滤器。

类规范模板

先放张上一篇博客提到的图,讲到后面我们需要参考。

过滤器

过滤器是什么呢?就是类似正则的规则过滤器。

首先我先把几种不同类型的过滤器用几张表格列出来,然后讲述一下功能。这一篇我能用话说明白的绝对不用代码。自我感觉在博客中放代码体验太差了。

类相关的过滤器

1. 类名过滤器
符号功能
?可以匹配任意一个字符,但是 package 的分隔符(.)除外,例如:com.example.T?st,可以匹配 com.example.Testcom.example.T2st,但是 com.example.T12stcom.example.Tstcom.example.T.st 不可以。
*可以匹配任意一部分连续的字符,但是 package 的分隔符(.)除外,例如 com.example.*Test,可以匹配 com.example.Testcom.example.AnyTest,但是 com.example.xxx.Test 不可以,还有一个特例 com.example.* 只能匹配当前包下的类,com.example.xxx.Test 就匹配不到。
**可以匹配任意一部分连续的字符,例如 com**,可以匹配 com.Testcom.example.Testcom.example.java.Test
在同一匹配规则中匹配和第 n 个通配符一致的内容。例如:*Any<1>,可以匹配到 TestAnyTestTestAnytest 不可以。
2. 字段和方法名过滤器
符号功能
匹配所有构造方法
匹配所有字段方法
匹配所有方法
*匹配所有方法和字段,包括构造方法。
?匹配方法名称中的任何单个字符。
在同一匹配规则中匹配和第 n 个通配符一致的内容。

注:

  1. 用法如下,就代表所有字段。

    -keep class DownloadClient {
     <fields>;
    }
  2. 用法如下,就代表所有方法。

    -keep class DownloadClient {
     <methods>;
    }

看到了 的用法,参照文章开始的类规范的图可知,我们可以再前面添加 public 之类的关键字,但是后面是添加不了东西的。
的后面必须存在一个参数列表。

3. 类型过滤器
符号功能
%匹配所有基本类型
?匹配类名中的任何单个字符
*匹配不包含包分隔符的类名的任何部分。
**匹配类名的任何部分,可能包含任意数量的包分隔符。
*匹配所有任何类型
...匹配任何类型任何数量的参数
在同一匹配规则中匹配和第 n 个通配符一致的内容。

注:?,* 和 ** 通配符不可以匹配原始类型,比如:int,float。包装类和普通类是可以匹配的,比如:Integer,String。

文件相关的过滤器

除了上述和类相关的过滤器之外还有和文件过滤器,如果需要自己独立使用 ProGuard 写配置规则或者需要写其他的配置,那么肯定要写那些文件需要输入,输出到哪等等的问题,往往我们的项目很大不能挨个文件去写规则,所以就需要过滤器。下一章我们介绍一些不常用的 ProGuard 选项,也能用到文件相关的过滤器。

符号功能
?匹配文件名中的任何单个字符。
*匹配不包含目录分隔符的文件名的任何部分。
**匹配文件名的任何部分,可能包含任意数量的目录分隔符。

小结

到此基本的过滤器我们也大概了解了,大家可以看到有好几个长得一样的过滤器,但是作用在不同地方功能不同,我们仔细去看他们的作用,其实也没有什么本质变化。
那么,我们回归一下先前的两个问题。

  1. 所有 Event 结尾的类里面的所有内容都不能混淆。

    -keep class **Event { *; }
  2. 所有 XXX 子类中的 x() 方法不混淆。

    -keep class ** extends XXX { 
     void x();
    }

但是 XXX 类被混淆了,如果不想 XXX 被混淆,那就在另写一条匹配规则。