为了尽可能减小应用的大小,我们通常会使用 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 就可以了。