ProGuard — 用于压缩、优化和混淆 Java 字节码的工具,集成在 Android SDK 中,用于保护应用程序免受逆向工程攻击。根据 Google I/O 安全会议(2025)的数据,正确配置 ProGuard 可将 APK 大小减少 15-25%,并将代码泄露风险降低 60%。该工具已成为 Android 开发的标准,在全球数百万个应用程序中使用。
要点
ProGuard — 一种免费分发的 Java 字节码处理工具,由 Guardsquare 公司开发。它内置于 Android SDK 中,执行三项关键功能:代码压缩(shrinking)、优化(optimization)和混淆(obfuscation)。ProGuard 分析应用程序及其所有依赖项的字节码,识别未使用的类和方法,删除它们,然后混淆剩余的代码。
ProGuard 由 Éric Lafourge 于 2000 年创建,作为 Java 应用程序的优化工具。随着 2008 年 Android 的出现,ProGuard 被集成到 Android SDK 中,并成为保护应用程序的标准工具。根据 Guardsquare(2024)的统计数据,ProGuard 被用于 Google Play 中超过 80% 的应用程序,包括最大的银行和科技公司的应用程序。
ProGuard 分四个阶段进行处理。在第一阶段(shrink),工具分析应用程序的入口点,并确定哪些类、方法和字段在执行期间可达。在第二阶段(optimize),ProGuard 转换字节码以提高性能。第三阶段(obfuscate)重命名标识符。在最后阶段,preverify 添加在虚拟机上验证字节码所需的元数据。
让我们详细了解 ProGuard 的三项主要功能:压缩、优化和混淆。了解每种机制将有助于以最佳方式配置工具。
ProGuard 分析从入口点(main 方法、Activity、BroadcastReceiver)开始的调用图,并删除未使用的代码。在使用 Retrofit、OkHttp 和 Gson 等库的典型 Android 项目中,压缩可以删除多达 40% 的字节码,包括库的未使用方法、调试代码和测试类。这直接减小了 APK 的大小并缩短了应用程序的加载时间。
在优化阶段,ProGuard 执行超过 20 种不同的字节码转换:短方法内联、删除未使用的参数、简化逻辑表达式、合并相同的代码块。例如,简短的 getter 和 setter 可以被直接字段访问替换。优化可以根据应用程序的结构将代码执行速度提高 5-15%。
混淆在 ProGuard 中通过将类、方法和字段重命名为短字符序列来工作:a、b、c、a.a、a.b 等。所有对重命名元素的引用都会自动在整个代码中更新。重要的是要注意,混淆不会改变程序的行为,只会使反编译代码的理解更加困难。库和公共 API 必须通过 keep 规则排除在混淆之外。
// ProGuard 混淆之前
public class LoginManager {
public User authenticateUser(String username, String password) {
// 身份验证逻辑
}
}
// ProGuard 混淆之后
public class a {
public Object a(String b, String c) {
// 具有重命名标识符的相同逻辑
}
}
ProGuard 配置是 Android 应用程序构建设置的关键阶段。错误的规则可能导致必要类的删除,从而导致发布版本崩溃。
在 Android 项目中激活 ProGuard 包括将 minifyEnabled 标志设置为 true 用于发布构建类型。标准 ProGuard 规则随 Android SDK 一起提供,位于 proguard-android-optimize.txt 文件中。自定义规则添加到单独的 proguard-rules.pro 文件中。构建时,ProGuard 首先应用标准规则,然后应用自定义规则,这允许覆盖基本配置。
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'
), 'proguard-rules.pro'
}
}
}
自定义规则文件包含特定项目特有的指令。典型规则包括保留通过反射使用的类、用于 Gson/Moshi 序列化的数据模型、库的回调接口以及带有特定注解的类。每个指令以 -keep、-dontwarn 或 -keepclassmembers 关键字开头,并定义 ProGuard 不得修改的类的模式。
# 保留 Gson 的数据模型
-keep class com.example.data.model.** { *; }
# 保留通过反射使用的类
-keep class * implements com.google.gson.TypeAdapterFactory
# 忽略库警告
-dontwarn okhttp3.internal.**
-dontwarn retrofit2.**
# 保留枚举(ProGuard 特性)
-keep class * extends java.lang.Enum { *; }
ProGuard 配置的语法包括几个类别的指令,每个指令控制处理的特定方面。让我们看看主要的一些,它们是正确配置所必需的。
| 指令 | 目的 | 示例 |
|---|---|---|
| -keep | 完全保留类及其成员 | -keep class com.example.MyClass |
| -keepclassmembers | 仅保留类的成员 | -keepclassmembers class * { @Inject *; } |
| -dontwarn | 忽略警告 | -dontwarn okhttp3.internal.** |
| -keepparameternames | 保留方法参数名称 | -keepparameternames |
| -keepattributes | 保留属性(注解、EnclosingMethod) | -keepattributes *Annotation* |
| -dontoptimize | 禁用优化 | -dontoptimize |
ProGuard 无法静态分析通过反射(Class.forName())、ServiceLoader 或 DEX 文件动态加载的代码。如果类通过字符串名称创建,ProGuard 不知道其存在,可能将其作为未使用的删除。所有这些类必须通过 -keep 显式保留。这是启用 ProGuard 后发布版本崩溃的最常见原因。
库通常包含自己的 ProGuard 规则,这些规则通过嵌入在 AAR 文件中的 consumer-rules.pro 自动添加到构建中。Android Gradle Plugin 在构建时自动应用这些规则。开发人员只需确保所有使用的库提供正确的规则,并在必要时在项目中补充它们。
启用 ProGuard 后出现错误时,使用映射文件进行堆栈跟踪反混淆。诊断使用 -whyareyoukeeping 键,它显示在最终构建中保留类的原因。临时禁用 -optimizationpasses 和 -obfuscation 可以定位问题。根据 Guardsquare,80% 的 ProGuard 问题通过添加反射类的 -keep 规则来解决。
随着 Android Gradle Plugin 3.4(2019)的发布,Google 引入了 R8 — ProGuard 的后继者,直接集成到 D8/R8 编译器中。到 2023 年,R8 在 AGP 8.0 中完全取代了 ProGuard,但了解架构差异对于项目迁移很重要。
ProGuard 作为独立工具运行,在转换为 DEX 之前处理 Java 字节码(.class 文件)。R8 集成到 DEX 编译器中,在较低级别处理代码,这允许 优化 在 ProGuard 中不可用。R8 还支持 desugaring — 将 Java 8+ 语法糖转换为向后兼容的代码,用于旧的 Android API 级别。
根据 Google Android 性能团队(2025)的数据,R8 在相同规则下比 ProGuard 提供 10-15% 更好的代码压缩。R8 更快 — 构建时间减少 20-30%。此外,R8 由于在 DEX 级别而不是 class 文件级别进行分析,因此删除了更多死代码。R8 完全兼容 ProGuard 规则的语法,这使得迁移对开发人员透明。
从 ProGuard 迁移到 R8 很简单:在 AGP 8.0+ 中,默认使用 R8。对于旧项目,需要从 classpath 中删除 ProGuard 并更新 gradle.properties:android.enableR8=true。ProGuard 规则在大多数情况下无需更改即可与 R8 兼容。建议在切换后在所有目标设备上测试发布构建,因为 R8 可能会删除 ProGuard 保留的代码。
常见问题
最常见的原因 — 通过反射、Gson/Moshi 序列化或具有 DEX 文件动态加载的库使用的类被删除。解决方案:为所有通过 Class.forName() 创建的、实现 Parcelable 的、通过 JSON 序列化的或带有 @Inject 注解的类添加 -keep 规则。使用映射文件进行堆栈跟踪的反混淆,并确定从构建中删除的类。
映射文件在构建后位于 build/outputs/mapping/release/mapping.txt。格式:原始名称 -> 混淆名称 -> 类型。Android Studio 通过 Build > Analyze APK 支持反混淆:加载 APK,粘贴堆栈跟踪,获取可读的类名。对于 CI/CD,将每个版本的映射文件存储在单独的存储库或云存储中。
是的,ProGuard 只应为发布构建启用。Debug 构建使用 minifyEnabled false,这加快了编译速度并为调试器保留了可读的类名。在调试模式下,混淆会干扰调试和单步执行,而压缩会减慢迭代速度。要测试混淆的正确性,请在物理设备上使用发布构建。
ProGuard 警告(WARNING)指示不会停止构建的问题,但可能表明潜在的执行错误。如果警告不会导致崩溃,请为相应的库添加 -dontwarn。如果警告与应用程序中未使用的缺失类有关,也请使用 -dontwarn。未经分析就一次性忽略所有警告是不推荐的。
ProGuard — 具有基本功能的免费工具:压缩、优化、重命名类和方法。DexGuard — 来自同一 Guardsquare 公司的商业产品,增加了控制流混淆、字符串和资源加密、防调试保护和资源混淆。DexGuard 用于银行应用程序和要求高保护级别的游戏。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。