Android Proguard工具使用和配置详解

Android开发中的Proguard

Proguard是Android开发时经常会用到的一个工具,在Android SDK中已经集成了一个免费的Proguard版本,位于<sdk>/tools/proguard目录中。

在Android项目中,通过修改module下面的build.gradle文件来开启使用Proguard选项,当开启了此选项后,Android Studio在编译该module时就会使用指定的配置来对编译之后的Java字节码进行处理,得到一个优化后的jar包。

在最新的Android Studio 2.1.2版本创建的Android工程中,module中的build.gradle有如下一段配置。这里的minifyEnabled即用来控制在编译时是否需要启用Proguard,将minifyEnabled修改为true,即表示启用Proguard。proguardFiles配置的是Proguard对此module进行处理时使用的配置文件,’proguard-android.txt’是Android SDK中自带的一个基本Progurad配置文件,它同样位于<sdk>/tools/proguard目录中,’proguard-rules.pro’则是当前module所在目录中的一个配置文件,默认是空白的,需要由开发者自行实现,当启用了Proguard之后需要编辑这个文件,向其中添加适合当前项目的Proguard配置。本文后面会对Proguard的配置进行详细解析。

android {
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

除了’proguard-android.txt’,Android SDK中还自带了另外一个Pruguard配置文件’proguard-android-optimize.txt’,它同样位于<sdk>/tools/proguard目录中,和’proguard-android.txt’ 的区别在于,’proguard-android-optimize.txt’中开启了Proguard optimize的选项(optimize是Proguard的一项功能),而’proguard-android.txt’ 中没有开启optimize选项。如果需要开启optimize,可以将这里的’proguard-android.txt’修改为’proguard-android-optimize.txt’。当然,如果熟悉Proguard的配置,也可以直接编辑module中的’proguard-rules.pro’,向其中添加optimize的配置。

这里有几点需要注意的地方:

  1. 在较早的Android Studio 版本中,这里的一些选项名字可能会有所不同,可能会看到runProguard这样的配置。这是因为Android Plugin for Gradle 在0.14.0版本中修改了DSL中的几个名字。虽然在实际项目中不会再使用这么老的版本了,但是网络上很多文章仍然是在老版本基础上写的。参考: http://tools.android.com/tech-docs/new-build-system

  2. Proguard虽然被集成到了Android SDK中,但是Proguard并非是Google开发的,他最早是个人开发者Eric Lafortune在业余时间做的一个开源项目,后来Eric Lafortune加入了GuardSquare(一家总部在比利时的公司)担任CTO,Proguard也被GuardSquare当做公司的产品来宣传,不过仍然是开源且免费的。 GuardSquare还有另外一款基于Proguard的产品Dexguard,这款产品是收费的,当然功能上也比Proguard要强大。从名字上就可以看出这款产品是专门针对Android APK优化的。

  3. 在Android Plugin for Gradle 2.0版本中集成了一个实验性质的工具,可以用来对代码进行shrinker,也就是可以去掉代码中没有用到的那些变量,方法和类。目前这个工具仍然是experimental,由于没有混淆和优化的功能,和Proguard相比没有任何优势。也许Google会继续开发,未来可能会像Android Studio取代Eclipse + ADT 一样,取代掉Proguard,当然也可能会废弃掉。
    要开启这个工具,可以参考 http://tools.android.com/tech-docs/new-build-system/built-in-shrinker

Proguard功能介绍

Proguard经常被看做Android平台的代码混淆工具,这种看法是比较片面的。Proguard项目诞生于2002年,而Android 1.0是2008年才发布的,也就是说早在Android发布之前Proguard就已经存在很久了。Proguard不仅适用于Android项目,也适合对其他使用Java开发项目的优化。此外,Proguard也不仅仅是一个代码混淆工具,代码混淆只是Proguard四项功能中的其中一项。它之所以被认为是Android平台的代码混淆工具,是因为Google将其集成到了Android SDK和Android项目的编译过程中,成为Android开发默认的代码优化工具,从而被广大的Android开发者所熟悉。此外,开发者对代码混淆功能的需求比其他功能要迫切的多,Proguard代码混淆功能成为开发者必选的一项功能。

Proguard帮助文档中是这样描述的

ProGuard is a Java class file shrinker, optimizer, obfuscator, and preverifier.

从这段话中可以看出,Proguard可以对Java class文件执行shrink,optimize,obfuscate和preverify四项优化。这四项优化也代表了Proguard的四项功能。

shrink

shrink功能的作用是移除代码中没有使用到的类,方法和成员变量,从而减少文件大小。

这些没有使用到的类,方法和成员变量的产生主要有两种情况,一种是自身代码中由于功能,需求的变更,或者代码的重构,导致原先的一些代码不再被使用,这些代码有时会忘记删除,有些则是故意保留下来,以备以后使用,这些被废弃或故意存留的代码对程序的运行没有任何用处。另一种是项目中经常会包含一些开源代码或第三方SDK,而项目通常只会使用到开源代码或第三方SDK中的部分功能,那些没有用到的功能对应的代码同样是可以去掉的。

所有这些没有用处的类,方法和成员变量,对应用的运行没有任何用处,但是它们都会被编译到jar文件中,不仅会增大jar文件的大小,而且在Android平台上可能还会导致方法数超过64k,引起一些不必要的问题。所以最好能够将这些没有用处的类,方法和成员变量都从jar文件中删除。但是,如果靠开发者手动删除是很费事费力的。一方面,如果第三方SDK不是开源的,或者项目没有将源码包含进来,是无法手动删除的。另一方面,手动从项目中删除代码是有副作用的,如果后面又需要用到这部分代码,又得费力加回来。因此,最好是可以有工具自动完成这项工作。

Proguard的shrink功能就是用来执行这项操作的,它会自动分析jar包中各个类,方法之类的调用和依赖关系,对那些没有用到的类,方法和成员变量进行剔除。

optimize

optimize过程会在Java字节码层面上进行优化,剔除方法中一些冗余的调用。帮助文档中列出了一些当前Proguard支持的优化。

对常量表达式进行计算
删除不必要的字段访问和方法调用
删除不必要的代码分支
删除不必要的比较和instanceof测试
删除未使用的代码块
合并相同的代码块
减少变量的分配
删除只赋值但没有使用的成员变量,以及未使用的方法参数
内联常量字段,方法参数和返回值
内联很短的方法和只被调用一次的方法
简化尾递归调用
合并类和接口
尽可能的将方法修饰为private,static和final
尽可能的将类修饰为static和final
移除只被一个类实现的接口
其他200多项细节优化,比如用 <<1 代替 *2 运算
删除Log的相关代码(可选)

obfuscator

obfuscator也就是Proguard最常被提到的代码混淆功能。由于Java代码编译之后的class文件中仍然包含了调试信息,源文件名,行号,类名,方法名,成员变量名,参数名,局部变量名等信息,通过反编译可以很容易的将这些信息还原出来。通过obfuscator,可以将jar包中不需要对外暴露的类名、方法名和变量名替换成一些简短的,对人来说没有意义的名字。

以下是一段代码混淆前和混淆后的对比。可以看到类名,成员变量名,方法名,参数名和局部变量名都被替换成了一些简单无意义的名字,从混淆后的代码中就很难理解原先代码的逻辑。然而这两段代码对计算机来说是完全等价的。此外,由于混淆后jar包中原先很长的名字被替换成了简短的名字,这使得jar包的体积更小了。这也是混淆带来的另一个附加的好处。

public enum Edge {
    private float mCoordinate;

    public static float getWidth() {
        return Edge.RIGHT.getCoordinate() - Edge.LEFT.getCoordinate();
    }

    public static float getHeight() {
        return Edge.BOTTOM.getCoordinate() - Edge.TOP.getCoordinate();
    }

    private static float adjustLeft(float x, Rect imageRect, float imageSnapRadius, float aspectRatio) {

        float resultX = x;

        if (x - imageRect.left < imageSnapRadius)
            resultX = imageRect.left;
        else {
            float resultXHoriz = Float.POSITIVE_INFINITY;
            float resultXVert = Float.POSITIVE_INFINITY;

            if (x >= Edge.RIGHT.getCoordinate() - MIN_CROP_LENGTH_PX)
                resultXHoriz = Edge.RIGHT.getCoordinate() - MIN_CROP_LENGTH_PX;

            if (((Edge.RIGHT.getCoordinate() - x) / aspectRatio) <= MIN_CROP_LENGTH_PX)
                resultXVert = Edge.RIGHT.getCoordinate() - (MIN_CROP_LENGTH_PX * aspectRatio);

            resultX = Math.min(resultX, Math.min(resultXHoriz, resultXVert));
        }
        return resultX;
    }
}
public enum a {
  private float e;

  public static float a() {
    return c.c() - a.c();
  }

  public static float b() {
    return d.c() - b.c();
  }

  private static float a(float paramFloat1, Rect paramRect, float paramFloat2, float paramFloat3) {
    float f1 = paramFloat1;
    if (paramFloat1 - paramRect.left < paramFloat2)
     
  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值