介绍
ProGuard 是一个 Java 类文件压缩器、优化器、混淆器和预校验器。压缩步骤,检查并删除没有被使用的类、字段、方法和属性。优化步骤,分析和优化方法的字节码。混淆步骤,用简短无意义的名称重命名剩余的类、字段和方法。这些步骤使得代码库变得更小、更有效并且更难进行逆向工程。最后的预校验步骤就是将预校验信息添加到类型,这是 Java ME 和Java 6以上版本所必须的。
这些步骤都是可选的。例如,Proguard 还可以用于列出应用程序中的死代码,或者预先验证类文件,以便在 Java 6 中有效地使用。
首先,ProGuard 读取输入jar (aars|war|ear|zip|apk|目录),然后对它们进行压缩、优化、混淆和预先校验。当然,您可以有选择的让 ProGuard 执行多次优化。最终,ProGuard 将处理后的结果输出到一个或多个目标jar (aars|war|ear|zip|apk|目录)。输入中可以包含资源文件,其名称和内容在混淆类名的过程中有选择地被更新。
ProGuard 需要指定输入 jar 的库 jar (aars|war|ear|zip|apk|目录),编译代码所需的库。ProGuard 用它们来重构正确处理所需的类依赖关系。库 jar 本身总是保持不变,应该将它们放在最终程序的类路径中。
入口点(Entry points)
为了确定哪些代码需要保留、哪些代码可丢弃或混淆,就必须为代码指定一个或多个入口点。这些入口点通常是带有 main 方法、applet、midlet、活动等的类。
- 在压缩步骤中,ProGuard 从这些入口点开始,递归查找并确定哪些类和类成员被使用,没有被使用的类及类成员则被丢弃。
- 在优化步骤中,ProGuard 进一步优化代码。这些优化包含,非入口点的类和方法可以设置为私有、静态或final,未被使用的参数可以被删除,还可以内联一些方法。
- 在混淆步骤中,ProGuard 重命名不是入口点的类和类成员。在整个过程中,保留入口点可以确保通过原始名称能访问它们。
- 预验证步骤是唯一不需要知道入口点的步骤。
反射
任何的代码自动处理,都会面临反射和自我检查这个特定问题。在 ProGuard 中,代码中动态创建或调用的类或类成员(即按名称)也必须指定为入口点。例如,Class.forName()
构造可以在运行时引用任何类。通常不可能计算出必须保留哪些类(以及它们的原始名称),因为类名可能是从配置文件中读取的。因此,必须在 ProGuard 配置中使用 -keep
选项保留他们。
然而,ProGuard 能检查并处理一下情形:
- Class.forName("SomeClass")
- SomeClass.class
- SomeClass.class.getField("someField")
- SomeClass.class.getDeclaredField("someField")
- SomeClass.class.getMethod("someMethod", null)
- SomeClass.class.getMethod("someMethod", new Class[] { A.class,... })
- SomeClass.class.getDeclaredMethod("someMethod", null)
- SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class,... })
- AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
- AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
- AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")
类和类成员的名称有可能不同,但实际上ProGuard可以识别它们的构造是相同的。被引用的类和类成员在压缩阶段被保留,而字符串参数在混淆阶段被适当地更新。
此外,如果有必要保留一些类及类成员,ProGuard 将提供一些建议。例如,ProGuard 会提示像 (SomeClass)Class.forName(variable).newInstance()
这样的构造方式。 这表明 SomeClass(接口或类)及其实现可能需要保留,这时需要相应的调整配置。
使用 -addconfigurationdebugging
打印出建议 ProGuard 配置信息。这对解决因为缺少某些配置而崩溃的问题很有帮助。
如果混淆的代码涉及很多反射,可能需要不断试错,以获取到正确的结果。