解决aar内含jar包冲突的方法

2019-08-14  本文已影响0人  tommwq

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日 建立文档。

上一篇 下一篇

猜你喜欢

热点阅读