More|More than one file was found with OS independent path 'lib/armeabi/**.so'分析和解决

【More|More than one file was found with OS independent path 'lib/armeabi/**.so'分析和解决】最近在编译一个JNI项目遇到了这样的错误:

Execution failed for task ':app:transformNativeLibsWithMergeJniLibsForRelease'. > More than one file was found with OS independent path 'lib/armeabi/libJniTest.so'

首先想到的依赖的多个aar库中包含了冲突的so文件,这种情况可以通过配置packagingOptions解决。但工程中并未依赖其他库,问题是如何出现的呢?
一、APK会打包哪些so?
首先先了解下APK中的so通常来自哪里:
1. 自身构建的native库 如果工程自身包含native代码,并使用CMake等工具构建,会生成对应架构的so库:
android { ... externalNativeBuild { cmake { ... abiFilters 'armeabi', 'armeabi-v7a' } } } ... externalNativeBuild { cmake { path "CMakeLists.txt" } } }

CMake工作目录在app/.externalNativeBuild/cmake/debug/armeabi下,我们在CMakeCahe.txt中可以看到,CMAKE_LIBRARY_OUTPUT_DIRECTORY默认值为:
CMAKE_LIBRARY_OUTPUT_DIRECTORY:UNINITIALIZED=/Users/tsia/xxx/xxx/app/build/intermediates/cmake/debug/obj/armeabi



则工程构建完之后,该目录下就会输出对应架构的so: More|More than one file was found with OS independent path 'lib/armeabi/**.so'分析和解决
文章图片
CMake输出目录 这些so会打包到APK中。
2.指定的外部库 如果我们依赖了一个外部的so,编译时我们可以通过add_library添加外部库依赖,如果我们希望打包的时候能一起带进来,需要在gradle中配置jniLibs的路径:
android { ... sourceSets { main { jniLibs.srcDirs = ["mylibs"] } } }

mylibs和gradle文件在同一目录下,是相对的路径。jniLibs.srcDirs就是告诉gradle那个目录下的so库要打到包中。

More|More than one file was found with OS independent path 'lib/armeabi/**.so'分析和解决
文章图片
上图我们可以看到,mylibs目录下的so并未被打到包中,但二级目录和二级目录下的so文件都被打进去了,二级目录名称除了架构目录也可以自定义,非.so后缀的文件不会被打进去。(当然,这里没有设置ndk.abiFilters)
3. compile的外部库 如果通过compile方式依赖一个外部aar库,其中的so会被打包到APK中。
二、问题分析
我的项目中只包含自己构建的native库,唯一不同的是我在CMakeLists.txt中指定了构建产物的输出目录:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

于是在src/main/jniLibs/${ANDROID_ABI}下也输出了一份so库:

More|More than one file was found with OS independent path 'lib/armeabi/**.so'分析和解决
文章图片
我们没有指定jniLibs库,为什么会有冲突呢?
虽然我们没有设置,但jniLibs.srcDir有默认值,通过 println "jniLibs.srcDirs= ${android.sourceSets.main.jniLibs.srcDirs}"打印发现:
jniLibs.srcDirs= [/Users/tsia/Documents/xxx/xxx/app/src/main/jniLibs]

CMake输出目录和jniLibs指向同一个目录,下面的so会被打包两次产生冲突,原来如此!
三、如何解决
方法1 jniLibs目录设为空,如指向其他目录也行,只要不要和CMake输出目录重合。
android { ... sourceSets { main { jniLibs.srcDirs = [] } } }

方法2 中配置打包规则,有冲突选择就第一个最为打包内容:
packagingOptions { pickFirst "**/libJniTest.so" }

相关链接:
《使用CMake构建Android JNI工程》

    推荐阅读