Proguard的Keep使用方法

背景知识

java代码存在互相引用的关系,构成一个网状关系.(个人理解)引用又分为两种:普通引用字符串引用(例如反射,native方法等).而java代码的执行入口点必然是采用的字符串引用(例如main等方法),因为外部想要执行此代码必须知道一个明确的入口点名字.

为了表述准确,类的成员变量下文称为域(Field),类的方法和成员变量统称成员(Member).

Proguard流程

  1. 压缩Shrunk:根据所有入口点建立引用关系网,去除网外的所有代码.

  2. 优化Optimize:对入口点以外所有的方法进行分析,将其中一部分方法变为final的,static的,private的或者内联的,从而提高执行效率.

  3. 入口点以外的类,方法,成员重构为简短的名字,可以进一步减小生成文件的大小并且混淆代码.

  4. Preverify:此处不讨论.

Proguard能够准确识别普通引用关系,但是只能识别部分典型的字符串引用(Class.forName方法等).而通过字符串引用的内容绝对不能改名字或者移除,所以就要使用keep配置proguard:

keep怎样使用

keep一共就有三个

  1. -keep [,modifier,…] class_specification

  2. -keepclassmembers [,modifier,…] class_specification

  3. -keepclasseswithmembers [,modifier,…] class_specification

其中modifier为可选配置,具体意义见下文,class_specification是类和成员的模板,用来指定应用keep规则的若干类及其成员.

  1. -keep可以保留指定的类名以及成员(就是把其当做应用的入口点)
  2. -keepclassmembers只能保留住成员而不能保留住类名
  3. -keepclasseswithmembers可以根据成员找到满足条件的所有类而不用指定类名(这样的类必定拥有所列出的所有成员),可以保留类名和成员名

不配置modifier时,上面的语句可以使保留的内容不被移除优化和混淆.

使用modifier

modifier共有三个可选值:

  • allowshrinking允许其被压缩,就是说指定的内容有可能被移除,但是如果没有被移除的话它也不会在后续过程中被优化或者混淆.
  • allowoptimization允许其被优化,但是不会被移除或者混淆(使用情况较少)
  • allowobfuscation允许其被混淆,但是不会被移除或者优化(使用情况较少)

class_specification规则

class_specification是类和成员的一种模板,只有符合此模板的类和成员才会被应用keep规则.

class_specification的一个复杂的示例
这里写图片描述

[]内的内容是可选的, …表示可以有若干个前面紧邻项.

  • class可以指代任意类和接口,interface指明为接口,enum指明为枚举,在interface或enum前添加!指明为非interface或非enum.
  • classname必须用全名,例如java.lang.String.可以使用正则表达式:

    • ?表示类名中的任意单个字符,但是不包括分割符(.)
    • *表示类名中的任意多个字符,但是不包括分隔符(.)
    • **表示类名中的任意多个字符,包括分隔符(.)

    可以在类名前面添加!为取非之意.为了兼容旧版本以及使用方便,类名*表示所有类(无论其在哪个包下).

  • extends和implements关键字是等价的

  • @指明类或成员具有某些注解

由于指定成员的规则较为复杂,下面单列一节

class_specification中指定成员

成员是java样式的,但是方法参数只有参数类型而没有参数名称,成员可以有以下样式(方法没有返回值,并且只有有参数列表)

  • <init> 代表任意构造方法.
  • <fields> 代表任意域.
  • <methods> 代表任意方法.
  • * 代表任意成员(包括成员变量和方法).

成员可以使用正则表达式

  • ? 代表方法中的任意单个字符.
  • * 代表方法中的任意多个字符.

成员部分描述类型时可以使用以下通配符

  • % 表示任意基本类型(int,char等,但是不包括void).
  • ? 表示类名中的任意单个字符.
  • * 表示类名中的任意多个字符,不包括分隔符(.).
  • ** 表示类名中的任意多个字符,包括分隔符(.).
  • *** 表示任意类型.
  • ... 表示任意多个任意类型的参数.

注意?*和**无法匹配基本类型,只有***可以匹配任意维度的数组类型,例如** get*()*可以匹配java.lang.Object getObject()但是不能匹配float getFloat()也不能匹配java.lang.Object[] getObjects()

构造函数既可以通过短名字(不含包名)指定,也可以通过全名指定.

另外三个keep

  • -keepnames class_specification是-keep,allowshrinking class_specification的缩写
  • -keepclassmembernames class_specification是-keepclassmembers,allowshrinking class_specification的缩写
  • -keepclasseswithmembernames class_specification是-keepclasseswithmembers,allowshrinking class_specification的缩写

这里写图片描述

Tips

一般如果不了解应该采用哪一个keep的话就使用-keep,它可以keep住类名和成员名不被移除和修改名字
注意:指定了keep的类而没有指定成员则只会keep类名,成员名有可能被移除或混淆

示例1

-keep class com.test.MainActivity$Item{
*;
}
可以keep内部类(仍然为内部类),类名和成员名都没有变

示例2

keep程序入口点

-keep public class mypackage.MyMain {
public static void main(java.lang.String[]);
}

参考资料

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值