【问题标题】:Android NDK - make two native shared libraries calling each otherAndroid NDK - 使两个本机共享库相互调用
【发布时间】:2013-07-15 07:54:36
【问题描述】:

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

决定把这个问题贴出来马上回答,让整个过程的Q和A在一起,希望别人不要再浪费一天的时间去研究了。

【问题讨论】:

    标签: android build android-ndk java-native-interface shared-libraries


    【解决方案1】:

    正确的构建过程相对容易,我的问题是使 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 := $(调用我的目录)
    包括 $(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

    包括 $(CLEAR_VARS)
    LOCAL_MODULE := mod2
    LOCAL_SRC_FILES := mod2/file1.cc
    LOCAL_SRC_FILES += mod2/file2.cc
    ...
    include $(BUILD_SHARED_LIBRARY)    # 这会构建 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);
        ...
    }
    

    事情开始变得像魅力一样......

    格雷格

    【讨论】:

    • 由此我们可以得出结论,系统的动态加载器会检查并加载依赖项(标准行为),而 Java 加载器不会。
    • 这是一个很好的观察,@Samveen。也许我应该在较新版本的 Android 下重新测试,看看他们最近有没有改进。
    • 感谢您的这篇文章,格雷格。这正是我所需要的,并为我节省了很多时间!
    猜你喜欢
    • 1970-01-01
    • 2018-02-20
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 2015-04-06
    • 1970-01-01
    相关资源
    最近更新 更多