Android打包里说的“混淆包”,通常不是一个全新的APK&AAB,而是指开启混淆后构建产物里配套产生的一组文件,核心是:
mapping.txt:混淆映射文件
它记录了:
原始类名/方法名/字段名 -> 混淆后的名字
比如:
com.xxx.user.UserProfilePresenter -> a.b.c:
void loadUserInfo() -> a
java.lang.String userId -> b
线上崩溃日志里可能看到:
at a.b.c.a(SourceFile:23)
拿到mapping.txt后,就可以还原成:
at com.xxx.user.UserProfilePresenter.loadUserInfo(UserProfilePresenter.java:23)
1. 为什么Android要混淆?
Android打包时,代码最终会变成.dex文件。别人可以通过反编译工具看到比较多的类名、方法名、业务逻辑。 所以Release包通常会开启:
minifyEnabled true
Android Release构建通常通过R8/ProGuard/DexGuard做三件事:
- 压缩Shrink 删除静态分析认为没有使用的类和方法。
- 优化Optimize 内联方法、删除无效代码、合并逻辑等。
- 混淆Obfuscate
缩短类名和方法名。例如:
Ortsession.run()可能变成: a.b()
普通Java调用会同步修改调用方,因此通常没问题。但下面这些动态调用无法通过静态分析识别:
- JNI/native调用
- 反射
- JSON序列化
- JavaScript接口
- XML中引用的类
- 通过字符串拼接得到的类名或方法名
这类代码通常需要-keep规则
2. 混淆包里最重要的文件:mapping.txt
开启混淆后,一般会在类似目录生成:
app/build/outputs/mapping/release/mapping.txt
有些项目也会把它打进一个zip,统称为:
mapping.zip
progurad.zip
symbol.zip
混淆包
不同公司叫法不同,但是核心都是为了保存Release包和mapping文件的对应关系。
3. 常见的混淆相关文件
开启R8/ProGuard后,常见有这些文件:
mapping.txt
最重要,记录原始名字和混淆名字的映射。
usage.txt
记录哪些类、方法、字段被删除了。
seeds.txt
记录哪些类和成员被保留,没有被混淆。
configuration.txt
记录最终生效的混淆规则。
4. 项目中怎么开启?
典型配置
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'
), 'proguard-rules.pro'
}
}
}
minifyEnabled true :开启代码压缩、优化、混淆。
shrinkResources true:删除未使用的资源,比如无用图皮、layout、string。
proguard-rules.pro:自己写的保留规则。
5. 为什么有些类不能被混淆?
因为有些代码依赖 反射、注解、序列化、JNI、路由、WebView JSBridge 等机制。
比如Gson:
class User {
String userName;
int age;
}
如果字段被混淆:
userName -> a
age -> b
服务端返回:
{
"userName": "Tom",
"age": 18
}
Gson就找不到字段了。
所以要么加注解:
@SerializedName("userName")
String userName;
要么写keep规则:
-keep class com.xxx.model.** { *; }
6. R8和ProGuard是什么关系?
现在Android Gradle Plugin默认使用的是R8。
ProGuard:老一代混淆/压缩工具 R8:Google新一代替代工具
但很多文件和配置名字还是叫:
proguard-rules.pro
proguardFiles
所以很多人习惯说“ProGuard混淆”。 实际上现在大多数Android项目是:
R8执行混淆+使用ProGuard规则语法