1 背景和问题
最近在尝试将两个SDK集成到一个App中。这两个SDK都是以aar格式提供的,在分别集成时都可以正常工作。但如果同时使用两个SDK,编译器会报告错误:
Execution failed for task ':transformClassesWithJarMergingForDebug'.
> com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: com/somewhere/over/the/Rainbow.class
原来这两个SDK都使用了同一个jar包,并将其打包在aar中。
2 解决
使用gradle和com.android.application插件编译Android工程时,gradle会将工程所依赖的aar包解压缩到目录
build\intermediates\exploded-aar
每个aar包独占一个子目录。通常发生冲突的文件会保存在libs、jars、jni等几个子目录下。找到并删除冲突文件,就可以解决编译问题。
gradle可能会重新对aar进行解压缩,因此手动删除是不行的。观察编译的错误消息,编译错误发生在任务transformClassesWithJarMergingForDebug中。只要为它增加一个处理冲突的前置任务就行了。
task clearDuplicatedClasses {
doFirst {
def buildDir = project.getBuildDir()
def files = ["\\intermediates\\exploded-aar\\MyAar\\jars\\libs\\Abc.jar",
"\\intermediates\\exploded-aar\\MyAar\\jars\\libs\\Def.jar",
"\\intermediates\\exploded-aar\\MyAar\\jars\\libs\\Xyz.jar"]
files.each {
delete new File(buildDir, it)
}
}
}
project.afterEvaluate {
project.tasks.findByName("transformClassesWithJarMergingForDebug").dependsOn(clearDuplicatedClasses)
}
transformClassesWithJarMergingForDebug是动态添加到project.tasks中的,如果直接写成
project.tasks.transformClassesWithJarMergingForDebug.dependsOn(clearDuplicatedClasses)
gradle会报告找不到任务。
3 思考和附录
如果我们需要在已有的jar包(或SDK)上做一些封装,并将封装后的代码作为SDK提供给客户。这时我们可以考虑构建两个版本,一个只包含封装层代码,另一个包含全部代码。以免出现jar包冲突的情况,方便客户使用。
如果想了解一个gradle任务依赖于哪些任务,可以使用-dry-run(或-m)参数执行这个任务:
.\gradlew.bat -m assembleDebug
Android gradle plugin插件位于$home.gradle\caches\modules-2\files-2.1\com.android.tools.build\gradle-core的子目录下,名字是gradle-core-X.Y.Z.jar。这个jar包没有混淆,可以使用反编译工具查看里面的代码。编译Android工程使用的很多任务都定义在里面。
使用gradle编译时,很多任务是动态添加的。要了解有project包含了哪些任务,以及这些任务对应的类,可以在build.gradle里增加一个任务
task listAllTasks {
doFirst {
println("========================================")
getProject().getTasks().forEach { task ->
print(task.getName())
print("\t")
println(task.getClass().getName())
}
println("========================================")
}
}
如果要查看某个特定任务对应的类,可以用
task printTaskClass {
doFirst {
println("========================================")
println(getProject().getTasks().findByName("prepareDebugDependencies").getClass())
println("========================================")
}
}
4 编辑记录
2019年07月30日 建立文档。
网友评论