java.lang.UnsatisfiedLinkError: dlopen failed: library not found

smi*_*itt 6 android build cmake shared-libraries android-gradle-3.1.0

我正在尝试使用 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)
Run Code Online (Sandbox Code Playgroud)

我通过了编译和链接步骤,android studio 继续在我的设备上安装 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)
Run Code Online (Sandbox Code Playgroud)

我知道这是因为 b_lib 的 .so 不在 apk 中。我可以在 a 的共享库中看到 b 的符号和 a 的符号。

所以我的问题是我怎样才能

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

我搜索了很多类似的帖子和问题(例如一个两个),但我无法找到任何工作。我真的在寻找正确的方法来做到这一点 - 将来不会产生问题(例如更改 targetSDKversion)。我还尝试使用最新的 ndk 版本构建预构建的库。

我可能犯了一个很小的错误,如果有人能指出它,我将不胜感激。

提前致谢

小智 6

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


小智 1

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

我和操作员有同样的情况,我尝试了很多不同的方法。其中之一是尝试使用早期版本的 NDK 编译项目(我认为是 r14b,但我不太确定)。我遇到了一个不同的错误,并设法找到了此处详细描述的问题,在“无效的 DT_NEEDED 条目”和“缺少 SONAME”部分中。此外,这个问题在这个特定问题中进行了描述,并且已经得到了充分的答案。

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

  • 将库包含在项目树的 jniLibs 文件夹中,以便将其打包在您的 APK 中,但不要链接到它。
  • 使用此处描述的任何方法追踪您想要合并的函数的符号。
  • 在 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;
Run Code Online (Sandbox Code Playgroud)

消费者保护计划

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.");
}
Run Code Online (Sandbox Code Playgroud)

如果操作正确,您现在应该能够照常使用这些功能。不过,我知道,如果您是初学者,这并不是最简单的过程。