ProGuard 怎么避免出现 a.a.a 冲突?

为了尽可能减小应用的大小,我们通常会使用 ProGuard 对应用代码进行压缩、混淆、优化。结果是代码中的类名、方法名、属性名都被更改了,未使用的代码会被移除,当然,你可以有选择的 keep 一些内容,比如包名、某些类、某些类里的方法、属性名。

在默认情况下,更改后的名称是 a、b、c 等等这样的名字,问题就出现了,A.aar 打包的时候一个不注意,漏掉了包名,出现了一个 a.a.a 的类,B.aar 打包的时候一个不注意,漏掉了包名,也出现了一个 a.a.a 的类。C 这个应用里用了 A.aar 和 B.aar,这就出事了,冲突了。

解决冲突

如果 ABC 都是同一个开发者开发的,那很好解决,直接在 AB 模块中修改混淆规则就可以了。

如果不是呢,作为 C 的开发者,怎么解决呢?

避免冲突

怎么避免出现 a.a.a 类?其实也好办,每次构建完成,去看一下产物不就可以了。

构建完成看 aar

直接看 aar 里有没有 a.a.a 这个类,但是这个方法也太暴力了,也很麻烦。

构建完成看 mapping.txt

去 mapping.txt 文件里看看,有没有 a.a.a 这个类,还好,容易了一些,那么要是需要再看看 a.a、a.a.a.a 呢?

构建完成使用 gradle 脚本检查 mapping.txt

上面的办法都太麻烦了,写个脚本处理吧,经过一番苦苦查询,终于找到一个处理 mapping 文件的类 proguard.obfuscate.MappingReader,show me the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import proguard.obfuscate.MappingProcessor
import proguard.obfuscate.MappingReader

task checkMappingContainsAaa {
doLast {
File mappingFile = new File(projectDir.getPath() + "/build/outputs/mapping/release/mapping.txt")
println mappingFile.getCanonicalPath()
MappingReader reader = new MappingReader(mappingFile)
Set<String> aaSet = new HashSet<String>() {
{
addAll("a", "a.a", "a.a.a", "a.a.a.a", "a.a.a.a.a", "a.a.a.a.a.a", "a.a.a.a.a.a.a", "a.a.a.a.a.a.a.a")
}
}
reader.pump(new MappingProcessor() {
@Override
boolean processClassMapping(String className, String newClassName) {
if (aaSet.contains(newClassName)) {
throw new Exception("混淆结果中含有:" + newClassName + ", 原类名为:" + className)
}
return false
}

@Override
void processFieldMapping(String className, String fieldType, String fieldName, String newClassName, String newFieldName) {

}

@Override
void processMethodMapping(String className, int firstLineNumber, int lastLineNumber, String methodReturnType, String methodName, String methodArguments, String newClassName, int newFirstLineNumber, int newLastLineNumber, String newMethodName) {

}
})
}
}

然后,当模块打包成 aar 之后,在执行这个 task 就可以了。