Proguard是一个代码优化和混淆工具。能够提供对Java类文件的压缩、优化、混淆,和预校验。压缩的步骤是检测并移除未使用的类、字段、方法和属性。优化的步骤是分析和优化方法的字节码。混淆的步骤是使用短的毫无意义的名称重命名剩余的类、字段和方法。压缩、优化、混淆使得代码更小,更高效。

一、Proguard简单使用:

在build.gradle文件中默认的Proguard文件配置是false关闭的,我们可以在debug模式下打开,设置为true即可:

android proguard 第三方jar proguard使用_混淆

默认的getDefaultProguardFile(‘proguard-android.txt’) 会使用build/intermediates/下面的文件(可以println 查看),如果在这个目录下没有则可以通过运行gradle任务:

android proguard 第三方jar proguard使用_内存优化_02

生成。而后面的’proguard-rules.pro’则是一个相对路径,相对于这个build.gradle同级目录下的proguard-rules.pro

然后在项目的gradle task中双击extractProguardFiles任务,就可以看到生成了三个混淆文件:

android proguard 第三方jar proguard使用_User_03

我们可以将proguard-android文件拷贝到我们项目中,看下系统生成的keep规则使用

可以看到不需要混淆的情况有如下一些:

  1. native层代码;
  2. 所有继承自view的组件;
  3. activity里面的所有view;
  4. 枚举;
  5. Parcelable;
  6. 静态内部类;
  7. webkit接口;
  8. 所有support包、androidx下面的类;
  9. 所有使用了keep注解的类、方法、成员变量、构造方法

简单语法:

-keep 指定类和类成员(变量和方法)不被混淆。

(保护了类名)
    -keep class com.dongnao.proxy.guard.test.Bug
(保护了 public static void的没有参数的函数)
    -keep class com.dongnao.proxy.guard.test.Bug{
           public static void *();
    }
 (保护所有)
    -keep class com.dongnao.proxy.guard.test.Bug{
           *;
    }

-keepclassmembers 指定类成员不被混淆(就是-keep的缩小版,不管类名了)。

(都被混淆了)
-keepclassmembers class com.dongnao.proxy.guard.test.Bug

-keepclasseswithmembers 指定类和类成员不被混淆,前提是指定的类成员存在。

(保护类名,但是没指定成员,所以函数名被混淆)    
-keepclasseswithmembers class com.dongnao.proxy.guard.test.Bug
-keepclasseswithmembers class       com.dongnao.proxy.guard.test.Bug{
        native <methods>;
  }

实验项目地址https://github.com/buder-cp/base_component_learn/tree/master/performanceOPT/buderdn08

开启混淆后,我们新建的User类,如果没有使用到,那么就不会打包到dex文件中,可以看到并没有User类:

android proguard 第三方jar proguard使用_内存优化_04

ProGuard 会移除所有(并且只会移除)未使用的代码。不过,ProGuard 难以对许多情况进行正确分析,可能会移除应用真正需要的代码。比如需要反射、动态加载所引用的类等情况,可能因为proguard移除或者混淆了这部分没使用的类,而导致错误。所以有时需要编写混淆优化配置文件。在gradle中的proguardFiles 能够让我们传递File文件或者文件路径交给proguard来执行。

当我们在MainActivity中使用这个User类后,

User user = new User();
        user.age = 18;
        user.name = "buder";

并且添加混淆规则,不混淆User类里面的String字段:

-keep class com.test.buderdn08.User {
    java.lang.String *;
}

最终的结果,User类中String成员变量是name,并没有被混淆掉。

android proguard 第三方jar proguard使用_内存优化_05

很多混淆命令可以多多尝试使用。

二、Proguard生成的混淆代码,如何对应原始代码

outputs/mapping/debug/mapping.txt文件中保存了混淆前后的对应关系,混淆后的代码错误栈恢复方法:

1.先配置  -keepattributes SourceFile,LineNumberTable

2.把错误信息保存到文件

3.使用工具 sdk/tools/groguard/bin/retrace.bat
再执行  retrace.bat  -verbose mappint文件  bug文件

我们在Activity中主动写一个bug,运行产生这样的日志:

android proguard 第三方jar proguard使用_User_06

这个日志有两个问题:

  1. 我们不知道是哪个类报告出的。
  2. 不知道这个类是哪一行报出的。

第一个问题很显然是由于我们的代码进行了混淆。

在开启混淆后,我们生成apk会产生

android proguard 第三方jar proguard使用_User_07

dump.txt:说明 APK 中所有文件的内部构。这个文件内容非常多,可性也并不高

mapping.txt:提供原始与混淆过、方法和字段名称之间的转换。

seeds.txt:列出未进行混淆的和成。我护的zip没有被混淆

usage.txt:列出从 APK 移除的代码。可以在这个文件中查看是不是有我们不想被移除的类

android proguard 第三方jar proguard使用_内存优化_08

最重要的当然就是我们的mapping文件了。通过这个文件我们就能定位到:

android proguard 第三方jar proguard使用_混淆_09

出现错误的函数是Bug类中的test函数。

我们现在比较简单,所以一下子就能定位到错误函数,如果函数层级较深,光靠自己比对来查找难度就比较大了。所以sdk中提供了一个工具在tools/proguard/bin中。

我们把logcat里面保存的错误堆栈复制到一个文件中:

android proguard 第三方jar proguard使用_内存优化_10

然后执行retrace 脚本(在 Windows 上为 retrace.bat;在 Mac/Linux 上为 retrace.sh)


retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]


android proguard 第三方jar proguard使用_混淆_11

android proguard 第三方jar proguard使用_内存优化_12

现在错误日志一句mapping还原了。所以我们每次在发布版本之后都需要保留这个mapping文件。所以一般异常上报平台都会提供mapping上传然后帮助我们分析。

第一个问题解决的,第二个问题是行数显示的是Unknow Source

android proguard 第三方jar proguard使用_User_13

如果希望出现具体行数,我们需要在配置文件中加入

抛出异常时保留代码行号,在异常分析中可以方便定位

-keepattributes SourceFile,LineNumberTable

android proguard 第三方jar proguard使用_proguard_14

另外还可以配合-renamesourcefileattribute AAA

使用字符串"AAA"来替代真正的类,避免泄漏更多的信息

android proguard 第三方jar proguard使用_User_15