More than one file was found wit

2019-03-09  本文已影响0人  tsia

最近在编译一个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: CMake输出目录

这些so会打包到APK中。

2.指定的外部库

如果我们依赖了一个外部的so,编译时我们可以通过add_library添加外部库依赖,如果我们希望打包的时候能一起带进来,需要在gradle中配置jniLibs的路径:

android {
    ...
    sourceSets {
        main {
            jniLibs.srcDirs = ["mylibs"]
        }
    }
}

mylibs和gradle文件在同一目录下,是相对的路径。jniLibs.srcDirs就是告诉gradle那个目录下的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库:

我们没有指定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工程》

上一篇下一篇

猜你喜欢

热点阅读