Android ProGuard:最激进的优化

Android ProGuard: Most Aggressive Optimizations

Android 的官方 proguard documentation 显示了两个主要优化:

这两个可能是最激进的设置吗?

我正在编写一个 android 库,需要确保当人们使用我的库时我的代码不会被破坏。 (我知道我可以在我的库中放置一些规则来对抗在使用该库的应用程序上设置的混淆器配置,但如果没有必要,我不想这样做。)

请记住,最好的 ProGuard 配置 - 是异常最少的配置。 我理解的异常情况下:

 -keepclassmembers class * extends android.content.Context {
    public void *(android.view.View);
    public void *(android.view.MenuItem);
 }

让我们浏览一下 proguard-android-optimize.txt 并查看 optimisation/obfuscation 个选项。

ProGuard 选项的详细描述我使用 this

-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* This - 可能的优化列表,!均值取反,所以不使用这个优化

-optimizationpasses 5 指定要执行的优化遍数。默认情况下,执行单次传递。多次通过可能会导致进一步的改进。如果优化通过后未发现任何改进,则优化结束。仅在优化时适用。
用法:OK,看起来默认 5 遍就足够了

-allowaccessmodification 指定 classes 和 class 成员的访问修饰符可以在处理期间扩大。这可以改善优化步骤的结果。
用法:OK,是的,看起来像是改进优化

-dontpreverify 当目标 Android 时,不需要预验证,所以不要预验证将其关闭以减少处理时间。但是这个选项对代码的牢不可破性没有影响。
用法:OK,只是为了减少处理时间

-dontusemixedcaseclassnames 指定在混淆时不生成大小写混合的 class 名称。默认情况下,混淆的 class 名称可以包含大写字符和小写字符的混合。这将创建完全可接受且可用的罐子。
用法:QUESTIONABLE,我找不到添加此选项的确切原因,但看起来将 class 名称从 abcdef 更改为 AbCdEf 不会'让代码牢不可破

-dontskipnonpubliclibraryclasses 指定不忽略非 public 库 classes。从 4.5 版开始,这是默认设置。
用法:OK,非常有用

proguard 不包含以下选项-android-optimize.txt:

-mergeinterfacesaggressively 指定可以合并接口,即使它们的实现 classes 没有实现所有接口方法...设置此选项会降低某些 JVM
上已处理代码的性能 Usage: BAD, look dangerous for Android, don't included into config, sumular of prohibition of class/merging/ in optimizations

-overloadaggressively 指定在混淆时应用主动重载。然后,多个字段和方法可以获得相同的名称,只要它们的参数和 return 类型不同,正如 Java 字节码所要求的(不仅仅是它们的参数,如 Java语言)
用法:BAD,Google 的 Dalvik VM 无法处理过载的静态字段。

所以我只知道一个更有用的混淆和非危险选项:
-repackageclasses ''

-repackageclasses '' 指定重新打包所有重命名的 class 文件,方法是将它们移动到单个给定包中。如果没有参数或使用空字符串 (''),包将被完全删除。此选项覆盖 -flattenpackagehierarchy 选项。
用法:OK,由 Google 使用,看起来我们至少找到了可以添加到配置中的选项

另请注意解码堆栈跟踪。 ProGuard 还会从堆栈跟踪中删除文件名和行号。这使得查找错误变得非常复杂。您可以通过将以下代码添加到配置中来保留行号:

-renamesourcefileattribute SourceFile 
-keepattributes SourceFile,LineNumberTable

这将保留行号,但将堆栈跟踪中的文件名替换为“SourceFile”。

另外不要忘记 ProGuard 看起来容易受到攻击,因为它不会加密字符串资源,因此请考虑使用 DexGuard 或自己加密重要的字符串(如令牌、url)。

根据优化文件的评论,优化会带来一定的风险,如果使用,必须对应用程序进行全面测试。根据我的经验,有必要禁用 code/simplification/advanced,因为它导致在 lambda 外部初始化的最终局部变量在 lambda 内部为 NULL。很难调试和查找。因此我的优化设置如下:

-optimizations !code/simplification/cast,!code/simplification/advanced,!field/*,!class/merging/*,!method/removal/parameter,!method/propagation/parameter

请注意,如果您的目标是 Android 2.0 及更低版本(这种可能性很小),则还必须禁用 code/simplification/arithmetic。除此之外,我还必须禁用 method/removal/parameter 和 method/propagation/parameter,因为它们会隐式启用 code/simplification/advanced(有关更多信息,请参阅 ProGuard manual)。

在libminecraft 1.15.2 zero中,第一个使用新libminecraft的advanced-technology whole-program优化的Minecraft版本,我只使用了gson,内联和代码优化。这样做是为了减轻使用完全优化的潜在不稳定影响。

现在我们有了 Minecraft 1.16.2 和 ProGuard 7.0.0,我使用了完全优化,没有任何稳定性问题(我实际上将 ProGuard 与 Allatori 堆叠在一起,但仍然没有 运行 出现稳定性问题,因为 Allatori 做得更好控制和数据流优化优于 ProGuard)。

我运行 ProGuard 在libminecraft 1.16.2 heavy 中三次。一次仅进行 5 次代码优化,一次进行所有 non-code 优化,一次仅进行 2 次代码优化。此外,我使用 Allatori 进行最后一次优化,因为它执行非常 high-quality 控制和数据流优化。