21

浪费了半天时间尝试从 jni 文件夹和子文件夹中的源构建两个共享库,例如mod1and mod2(Android NDK 编译为libmod1.soand libmod2.so),然后让 mod1 从 mod2 调用一个函数。关于如何使构建工作的大量答案,但是运行时动态链接不起作用,应用程序在启动时崩溃。

决定发布这个问题并立即回答它,以便整个过程的Q和A在一起,希望其他人不会再浪费一天的时间来研究它。

4

1 回答 1

26

正确的构建过程相对简单,我的问题是使 libmod1.so 依赖于 libmod2.so 在启动时导致不满意的链接 - mod1 代码找不到 mod2 共享库,即使两者都存在于最终 APK 的同一文件夹中,在 libs/armeabi、libs/x86 等下。但是,为了使我的答案完整:

  • 将 C 或 C++ 源代码和头文件放在 Android 项目中 jni 目录的子目录下,例如文件夹 mod1/ 和 mod2/

  • 根据 NDK 指令,创建 Application.mk 文件,例如我的是:

NDK_TOOLCHAIN_VERSION=4.7
APP_PLATFORM := android-8
APP_ABI := armeabi armeabi-v7a x86

  • 按照此模板创建 Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := mod2 # 这使得 libmod1.so 依赖于 libmod2.so
LOCAL_MODULE := mod1
LOCAL_SRC_FILES := mod1/file1.c
LOCAL_SRC_FILES += mod1/file2.cpp
...
include $(BUILD_SHARED_LIBRARY) # 这实际上构建了 libmod1.so

include $(CLEAR_VARS)
LOCAL_MODULE := mod2
LOCAL_SRC_FILES := mod2/file1.cc
LOCAL_SRC_FILES += mod2/file2.cc
...
include $(BUILD_SHARED_LIBRARY) # this构建 libmod2.so

就是这样,所有构建都没有抱怨 ndkbuild 脚本。您只需要一个 C 包装器就可以从 Java 调用一些函数。这是我的问题。因为我只有在 libmod1.so 中有可从 Java 调用的函数,所以我在 Java 中的 C 包装类就像:

public class CWrapper {
    static {
        System.loadLibrary("mod1");
    }
    public static native int func1(String aParam);
    ...
}

这对我来说似乎完全合乎逻辑 - 我从 Java 调用 libmod1.so,所以我使用了 System.loadLibrary("mod1"),并且由于 libmod1.so 知道它依赖于 libmod2.so,并且两个文件都在同一个文件夹中, libmod1 会知道如何查找和加载 libmod2,对吗?错误的!它在应用程序启动时因“不满意的链接”而崩溃。确切的错误消息是:

java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_link_image(linker.cpp:1635): could not load library "libmod2.so" needed by "libmod1.so"; caused by load_library(linker.cpp:745): library "libmod2.so" not found

我到处寻找更多代码添加到 Android.mk 以解决这个问题。终于尤里卡了!我修改了我的 CWrapper 类如下:

public class CWrapper {
    static {
        System.loadLibrary("mod2"); // must be first, as mod1 depends on mod2!
        System.loadLibrary("mod1");
    }
    public static native int func1(String aParam);
    ...
}

事情开始像魅力一样开始运作......

格雷格

于 2013-07-16T23:22:30.123 回答