JNI 同一个Module生成多个so

本文分享了在CMake环境下,同一模块生成两个SO文件时遇到的编译错误及解决方案。通过调整CMakeLists.txt文件结构,将每个SO对应独立的CMakeLists.txt,避免了编译冲突,成功实现目标。

终于解决困扰了我2天的问题!!!那叫一个激动!!

最近做一个项目,需要在同一个模块中同时生成两个SO文件,刚开始没觉得怎么样,就在一个CMakeLists.txt文件这么写入:

cmake_minimum_required(VERSION 3.4.1)
# 这是第一个so
add_library(
    a-lib
    SHARED
    a.cpp
)
# 这是第二个so
add_library(
    b-lib
    SHARED
    b.cpp
)
find_library(
    log-lib
    log
)
# 链接
target_link_libraries(
    a-lib
    b-lib
    ${log-lib}
)

AS的确帮我生成了2个so,用起来也没有问题,一切显得很正常,不过只是表象。

OK,接下来我需要在第二个so中打印时间,测试效率,so我在b.cpp中引入#<android/log.h>,使用print:

#include <android/log.h>
#define TAG "LOGGER"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
void doSomething() {
	...
	LOGE("time --- ");
	...
}

保存,编译,结果出现:

undefined reference to '__android_log_print'

然后我检查了很久CMakeLists.txt文件是否写错,结果调试了很久,又上网百度了很久,不得而解!我就纳闷了,就这样半天就过去,心情down到冰点!

另辟蹊径,新建了一个模块,单独把b.cpp文件拿出来,在新的模块中进行调试,没想到编译竟然通过了,运行也没有问题,这我才意识到,估计是哪里有冲突(目前我对C的编译还不是很熟悉,知道为什么的朋友麻烦告知一下)!

知道问题就好解决了,百度一下怎么在一个模块同时生成2个so的方法,参考 Android Studio NDK CMake 指定so输出路径以及生成多个so的案例与总结,最后成功。虽然成功,不过这篇博客还是有点问题的,下面说说我集成的过程。

首先看看我的项目某个模块的结构:
在这里插入图片描述

我们cpp下分了2个子文件夹,每个子文件都有自己的cpp源文件和CMakeLists.txt,这里注意了,每生成一个SO都需要一个CMakeLists.txt文件。

我们主要看CMakeLists.txt内容:

# 模块下CMakeLists.txt

# 指定版本
cmake_minimum_required(VERSION 3.4.1)

# 这里与参考博客不一样
# 添加子文件夹
ADD_SUBDIRECTORY(src/main/cpp/gles)
ADD_SUBDIRECTORY(src/main/cpp/yuv)
# cpp/gles/CMakeLists.txt

# 这里不再需要 cmake_minimum_required

add_library(
    gles-lib
    SHARED
    gles-lib.cpp
)

find_library(
    log-lib
    log
)

target_link_libraries(
    gles-lib
    ${log-lib}
)
# cpp/yuv/CMakeLists.txt

# 这里不再需要 cmake_minimum_required

add_library(
    yuv-lib
    SHARED
    yuv-lib.cpp
)

find_library(
    log-lib
    log
)

target_link_libraries(
    yuv-lib
    ${log-lib}
)

编译,就可以在build文件夹看到生成的so文件:
在这里插入图片描述

虽然问题很小,却困扰我许久,看来我还得加强在NDK方面的知识!

<think> 首先,用户的问题是:“我想查询一个包含JNIso库再包含另一个有JNIso库是否会产生冲突问题 请问包含JNIso库再包含另一个有JNIso库是否会有冲突问题”。这涉及到Android开发中JNIso库的使用。 从系统级指令中,我知道: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户提供了几个引用: - [^1]: 关于JNI调用第三方so库的方法。 - [^2]: 关于aar包中和jniLibs中的so库冲突。 - [^3]: 关于同一Module生成多个so文件。 - [^4]: 解决多个库的jni架构平台支持冲突。 用户的问题核心是:当一个so库(包含JNI)包含另一个so库(也包含JNI)时,是否会有冲突。 基于我的知识和引用: - JNI(Java Native Interface)允许Java代码调用本地代码(如C/C++编译so库)。 - so库是共享对象文件,在Android中是动态链接库。 - 冲突可能发生在多个方面:函数名冲突、符号冲突、架构冲突等。 从引用中: - [^3]提到在同一个Module中可以生成多个so文件,比如a-lib和b-lib,并且可以链接它们(target_link_libraries)。 - [^2]和[^4]讨论了so库冲突,特别是当多个库提供同一架构的不同版本时,比如armeabi vs armeabi-v8a。 关键点: 1. **符号冲突**:如果同一个Module中有多个so文件,它们可能定义相同的函数或全局变量,导致链接或运行时冲突。 - [^3]展示了如何在同一CMakeLists中定义多个so文件,并链接它们;如果没有冲突的符号,应该没问题。 2. **架构冲突**:当应用集成多个库时,可能有不同的架构支持(如armeabi, arm64-v8a),导致UnsatisfiedLinkError(如[^4]描述)。 - 解决方法包括在build.gradle中指定兼容架构。 3. **包含关系**:用户问的是“一个so库包含另一个so库”。在JNI中,一个so库可以依赖另一个so库(通过动态链接)。 - 如果两者都定义了相同的JNI函数(Java_com_example_method),可能会有冲突。 - 否则,如果函数名不同,应该没问题。 冲突的可能性: - **运行时冲突**:如果两个so库都实现了相同的JNI函数名,Android会加载第一个找到的,可能导致不可预测行为或崩溃。 - **链接冲突**:在编译时,如果链接不当,可能有未定义符号错误。 解决方法: - 确保每个库使用唯一的包名或前缀,避免JNI函数名冲突。 - 使用动态链接时,通过dlopen等加载库,控制加载顺序。 - 在build.gradle中配置abiFilters(如[^4])来只加载兼容架构。 回答结构: - **清晰结构**:逐步解释。 - **中文**:全部用中文。 - **引用**:引用相关部分自然添加标识。 - **相关问题**:生成相关问题。 生成相关问题: - 基于主题:如何避免so库冲突?多个so库共存的最佳实践?如何处理JNI函数名冲突? 构建回答: 1. **介绍问题**:直接回应用户查询。 2. **解释潜在冲突**:使用引用支持。 3. **解决方法**:提供建议。 4. **结论**:总结是否有冲突。 5. **相关问题**:在末尾添加。 确保行内数学表达式正确:如果有变量等使用$...$,但用户问题中没有数学内容,所以可能不需要。但如果有,如函数名,可以写成行内格式。 最终回答框架: - 开头:是的,可能会有冲突,但不是绝对的。 - 详细解释冲突类型。 - 引用[^3]和[^4]来支持。 - 给避免冲突的建议。 - 结尾:相关问题。 </think> ### 包含JNIso库再包含另一个有JNIso库是否会有冲突问题 在Android开发中,当一个包含JNIso库(共享对象文件)再依赖于另一个包含JNIso库时,确实可能产生冲突,但这不是必然的。冲突主要源于符号重复、加载顺序或架构兼容性问题,而非JNI本身。以下是基于引用内容的逐步分析(引用已整合到相关段落末尾): 1. **潜在冲突的原因**: - **符号冲突**:如果两个so库定义了相同的全局函数或JNI方法名(例如,`Java_com_example_method`),Android在运行时可能加载错误的实现,导致崩溃或未定义行为(如`UnsatisfiedLinkError`)[^1][^3]。引用[^2]也提到,当多个库提供同名so文件时,会发生类似冲突。 - **架构兼容性问题**:不同so库支持的CPU架构(如armeabi, arm64-v8a)可能不匹配。例如,一个库只支持armeabi,另一个支持全平台,在特定设备上运行时可能崩溃(如引用[^4]描述的Realm和高德地图冲突)[^4]。 - **加载顺序冲突**:系统加载so库的顺序不可控。如果主库依赖的副库先加载,可能覆盖关键符号,引发不稳定行为[^1][^3]。 2. **如何避免冲突**: - **唯一命名和前缀**:为每个so库的JNI函数添加唯一包名前缀(如`Java_com_module1_method`),确保符号不重复(引用[^1]建议在封装层自定义JNI函数)[^1]。 - **动态链接控制**:在CMakeLists或Android.mk中,明确指定so库的依赖关系。例如,引用[^3]显示如何在同一个Module链接多个so库(如`target_link_libraries(a-lib b-lib)`),但需确保符号无冲突[^3]。 - **架构过滤**:在build.gradle中配置`abiFilters`,只打包兼容架构(如仅保留armeabi-v7a),避免平台冲突(引用[^4]提供的解决方法)[^4]。示例代码: ```groovy android { defaultConfig { ndk { abiFilters 'armeabi-v7a' } // 指定单一架构 } } ``` - **隔离加载**:使用`System.loadLibrary()`动态加载副库,或在Java层通过反射控制初始化顺序,减少运行时干扰[^1]。 3. **结论**: - **冲突可能发生**:在符号重复或架构不匹配时,肯定会冲突(概率较高)。 - **可避免**:通过良好设计(如唯一命名、架构管理),可以安全共存(引用[^3]演示了多so库共存)[^3]。 - 实际开发中,建议使用工具如`nm`检查so库符号表,或用Android Studio的APK Analyzer验证架构兼容性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值