英文原版在这


翻译:

ProGuard是一个java类文件工具,有着压缩(shrink),优化(optimizer),obfuscator(混淆),preverify(预校验)功能。压缩步骤会检测当前程序中没有用到的类、方法和属性并且删除(从而达到减小代码体积的效果)。优化步骤分析方法中的字节码并且加以优化。混淆步骤原理是,将程序中保留着的类、方法等用没有意义的更短的名字替代其原有名字。最终步骤是预校验,在类中添加预校验的信息,这个功能是Java6以及更高级的版本所要求的。


这几个步骤都是可选项(根据不同的参数,Proguard采取不同的方式)举例来说,ProGuard也可以仅仅用作显示应用程序中的代码,或者是在Java 6中用来给类文件进行预校验。


首先,ProGuard会读取输入的文件(一般格式为jars,aars,wars,ears,zips,apks,或者是目录directories),然后先进行第一次压缩(原文是subsequently shrinks,可以理解为初级压缩,看过ProGuard源码知道,压缩会有很多次的)优化,混淆,预校验。也可以选择让ProGuard对程序进行多次优化。Proguard会将处理好的结果输出到文件(一般格式为jars,aars,wars,ears,zips,apks,或者是目录directories)。输入可能包含源代码文件,这些源代码文件的名字和内容可以有选择性的进行混淆,并且将反映到输出的类名称之中。


入口点(Entry points)

为了确定哪些代码需要保留,哪些代码可以被删除或者是混淆,我们需要确定程序的一个或者是多个入口点。这些入口点一般是典型的主要方法、程序、活动(midlets应该是Mobile Information Devices applet,移动信息设备小程序)等。

压缩阶段,ProGuard会从这些入口点开始(相当于初始化,以这些入口点为种子进行标记,它们是使用过的),递归的判断哪些类和类方法是使用过的。其他的类和类方法都是可以删除的

优化阶段,进一步地,ProGuard开始优化代码。在优化过程中,哪些不是入口点的类和方法可能会被修改成private,static,final等属性,没有用到过的参数会被删除,有些方***被改成inline方法(内联函数)

混淆阶段,ProGuard会对不是入口点的类和类方法进行重命名,在这个过程中,保留入口点的名字是为了确保入口点是可以正常访问到的。(重命名入口点会导致程序找不到入口点而运行错误)

预校验阶段,是唯一不需要知道入口点的一个阶段


使用方法(Usage section)这一节会给出大量的例子,来解释说明keep选项和其他例子功能的使用方法


反射(Reflection)

在任意代码的执行调用过程中,反射的存在会导致出现一些特定的问题。在ProGuard中,类和类成员也是需要和入口点一样,是以确定函数名称的方式,特定的被创建、被调用的。举例来说,Class.forName()可能在运行时会构造出任意某个类。因为类名称可能是从某个配置文件中所读取到的,那么通常来说,以它们特定的名称来猜想哪些类是可能要被保留下来的做法是不可能的。所以,你应该在ProGuard的配置文件中,使用一些简单的keep规则,来规范好类和类成员(来避免出现歧义等情况导致程序不可执行)

ProGuard已经提供给了如下的例子:

Class.forName("SomeClass")
SomeClass.class
SomeClass.class.getField("someField")
SomeClass.class.getDeclaredField("someField")
SomeClass.class.getMethod("someMethod", new Class[] {})
SomeClass.class.getMethod("someMethod", new Class[] { A.class })
SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

类和类成员的名字显然是不同的,但是ProGuard在识别过程中可能会是一样的(导致出现问题)相关的类和类成员在压缩阶段需要被保留,并且在混淆阶段需要对字符串的参数进行分类处理(原文是properly updated,意思是需要认真对待,不要出错)

进一步地,ProGuard对某些必需要保留的类和类成员提出了一些建议。ProGuard会留意这种框架,(SomeClass)Class.forName(variable).newInstance()可能会有一些提示和例子说这种类需要保留,根据这些提示你可以调整你的配置文件来使用ProGuard


为了在使用ProGuard之后得到比较好的结果,你需要对你正在处理的代码有一定的熟悉程度。混淆后的代码可能在反射调用和处理上有错误,尤其是当代码内部的重要信息丢失的时候,很有可能出现严重问题。