java.lang.UnsatisfiedLinkError: dlopen 失败:找不到库

11

我正在尝试使用cmake构建我的Android(原生)项目(将其从gradle实验性插件迁移,以前可以成功地构建和运行)。

我有一些本地代码(将其称为'a'),它使用另一个外部预构建库代码(将其称为'b'),我像这样链接了这两个: (根据https://developer.android.com/studio/projects/configure-cmake

cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -frtti -fno-common -fexceptions")

include_directories(
#a's include files' paths
#b's include files' paths)

file(GLOB_RECURSE A_SOURCES
#a's source files' paths)

add_library(a_lib SHARED ${A_SOURCES})
add_library(b_lib SHARED IMPORTED)
set_target_properties(b_lib PROPERTIES IMPORTED_LOCATION "b's .so path")
target_link_libraries(a_lib b_lib)

我通过编译和链接步骤,安卓工作室继续在我的设备上安装APK。然而,在启动后,应用程序在logcat中显示以下内容并冻结:

E/ExceptionHandler: Uncaught Exception java.lang.UnsatisfiedLinkError: dlopen failed: library "libb_lib.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1530)
at com.mm.projectname.model.libloadingclassname.<clinit>(libloadingclassname.java:99)

我知道这是发生的原因,因为apk中没有b_lib的.so文件。而且我可以在a的共享库中看到b的符号和a的符号。

我的问题是如何:

  1. 将预构建的b的.so文件打包到apk中
  2. 或防止系统在libs文件夹中查找b.so,并让它只在a的.so文件中查找b的符号。

我搜索了很多类似的帖子和问题(例如一个两个),但我什么都做不好。我真的在寻找正确的方法-不会在未来创建问题(例如更改targetSDKversion)。我还尝试使用最新的ndk版本构建预构建库。

有可能我犯了一个非常小的错误,如果有人能指出来,我会非常感激。

提前感谢


如果将libb_lib.so放入jniLibs中应该就足够了。预构建库是为哪些架构构建的,您正在为哪些架构构建,并且您尝试使用的设备的架构是什么? - Someone
存在一个加载库的问题。尝试使用fileTree(dir: 'libs', include: '**/*.so')compile fileTree(dir: 'libs', include: ['.jar','.so']) - Akhila
我认为如果你将libb放在这里:src/main/jniLibs/armeabi-v7a/libb_lib.so,那应该是可以的。因为这是从默认位置复制本地库的位置,所以你不需要在gradle中添加任何内容。 - Someone
@Akhila 我尝试把第二个命令放在 build.gradle 的依赖项中 - 但是那没起作用 - 不知道第一个命令在哪里? - smitt
你能把你的应用程序 build.gradle 添加到你的问题中吗? - Someone
显示剩余2条评论
2个回答

5
  1. 在您的应用程序的main目录(例如:/app/src/main)中创建一个jniLibs文件夹。
  2. 根据体系结构命名另一个文件夹。
  3. 将您的.so文件存储在此文件夹中。

如果我的本地库位于Java库的aar文件中怎么办? - Kamil
谢谢!这对我很有帮助,在 Kotlin Android 项目中。 - 孙欣乐
@Kamil 我也有同样的问题。你有什么解决方案吗? - Pravin Suthar
@PravinSuthar。在我的情况下,问题类是RFID扫描仪的驱动程序。它们在x86平台上无法工作。当我在x86模拟器上启动我的应用程序(以获得更好的性能)时,我会遇到“未满足的链接错误”。我不得不在该库和我的应用程序之间创建一个外观。在我的外观内部/后面,我捕获这些错误并提供一个虚拟类代替真实类。这些驱动程序是针对arm架构编写的。 - Kamil

1

有点晚了,但可能会有其他人感兴趣(甚至是楼主)。

我遇到了与楼主相同的情况,并尝试了许多不同的方法。其中之一是尝试使用较早版本的NDK(我认为是r14b,但我不完全确定)。我得到了一个不同的错误,并设法跟踪到了问题,该问题在这里中有很好的描述,分别在“无效的DT_NEEDED条目”和“缺少SONAME”部分。此外,该问题在这个特定的问题中也有描述,并已得到了充分的回答。

如果您无法重新编译正在使用的共享库以包含SONAME,就像我的情况一样,您可以做以下我所做的事情,并设法使其工作:

  • 将库包含在jniLibs文件夹中,这样它就会被打包到您的APK中,但不要链接它。
  • 使用任何描述here的方法跟踪要合并的函数的符号。
  • 在您的C++代码中创建相应函数的函数指针。
  • 在运行时加载共享库并映射到这些函数。

示例代码

头文件

private:

typedef uint32_t (*InitX_t)();
typedef uint32_t (*DoX_t)();
typedef uint32_t (*GetX)(uint32_t, char*);

InitX_t InitX;
DoX_t DoX;
GetX_t GetX;

CPP

void *handle = dlopen("libMyLib.so", RTLD_NOW);

if(handle == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load library");
}
else
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Library loaded");
}

InitX = (InitX_t)dlsym(handle, "InitX_SYMBOL");
ScanX = (ScanX_t)dlsym(handle, "ScanX_SYMBOL");
GetX = (GetX_t)dlsym(handle, "GetX_SYMBOL");

if(ScanX == nullptr || GetX == nullptr || InitX == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load functions.");
}

如果正确执行,现在您应该能够像往常一样使用这些功能。但是我知道,对于初学者来说,这并不是最简单的过程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接