【问题标题】:Android NDK linkingAndroid NDK 链接
【发布时间】:2012-05-22 13:35:42
【问题描述】:

我正在尝试构建一个调用 C++ 后端的 android 应用程序。此后端使用 ZeroMQ 进行消息传递。根据 ZeroMQ 指南上的 android build page,我构建了一个 ndk 版本 6 的本机工具链,并使用它(成功)构建了 ZeroMQ。

但是,当我使用 JNI 实现构建自己的 .so 时,我似乎无法正确加载所有内容。具体来说,如果我调用 System.LoadLibrary("zmq"),这会成功完成,但是如果我随后调用 *System.LoadLibrary("my_lib")* 我总是会收到 UnsatisfiedLinkError 抱怨:

Cannot load library reloc_library[1244]:   29 cannot locate zmq_msg_init'...

libmy_lib.so 已通过几种不同的方式生成,但均未成功。生成它后,我总是将 libmy_lib.so(以及 libzmq.so)复制到我的 android 项目的文件夹:libs/armeabi/。

# Compile all object files - this part was done for all options
/opt/android-toolchain/bin/arm-linux-androideabi-g++ -fpic -c Client_Events.cpp \
Client Wrapper.cpp jni.cpp -I /opt/android-root/include/ -I /my/project/specific/stuff

# Option 1 - Don't link the lib in at all
/opt/android-toolchain/bin/arm-linux-androideabi-g++ -shared  \
-Wl,-soname,libmy_lib.so -o libmy_lib.so jni.o Client_Events.o Client_Wrapper.o 

# Option 2 - Link ZeroMQ in statically
/opt/android-toolchain/bin/arm-linux-androideabi-g++ -shared  \
-Wl,-soname,libmy_lib.so -o libmy_lib.so jni.o Client_Events.o Client_Wrapper.o \
libzmq.a libstdc++.a -Wl,--whole-archive    

# Option 3 - Explicitly link ZeroMQ in dynamically
/opt/android-toolchain/bin/arm-linux-androideabi-g++ -shared  \
-Wl,-soname,libmy_lib.so -o libmy_lib.so jni.o Client_Events.o Client_Wrapper.o \
-L /opt/android-root/lib/ -lzmq

对于这些选项中的每一个,我都尝试在加载我自己的库之前显式调用 System.LoadLibrary("zmq") 而不是。结果没有任何变化。使用 nm 可以确认,至少在选项 #2 的情况下,缺少的符号 *zmq_msg_init* 确实存在于 libmy_lib.so 中。

关于为什么找不到链接的 ZeroMQ 符号有什么想法吗?

【问题讨论】:

  • 通过将其作为 zip 文件打开来检查您的库是否确实在 apk 中,并检查它在安装到设备上时是否实际被提取(在模拟器上可能最简单)。还考虑在您有一个没有依赖关系的本机库的情况下运行测试 - 即,从 hello-jni ndk 示例中获取一个。
  • 我可以通过静态链接 zeroMQ 来解决这个问题,但 c++ stl 支持动态。

标签: android android-ndk shared-libraries


【解决方案1】:

我刚刚学会了如何编译第二个库并将其链接到我在 android ndk 中的主库。让我看看我对你有没有用。

以下是我如何创建我的第二个库(在我的例子中,我将子弹物理库和 irrlicht 渲染引擎构建为我的游戏的 2 个独立库)。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := HEADER FILES 
LOCAL_MODULE := bullet
LOCAL_SRC_FILES := SRC FILES

LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -O3 -DANDROID_NDK -DDISABLE_IMPORTGL
LOCAL_LDLIBS := -ldl -llog

include $(BUILD_SHARED_LIBRARY)

然后将您的 libxxxx.so(在我的情况下为 libbullet.so 和 libirrlicht.so)复制到您的 jni 文件夹。并在您的主库 .mk 文件中添加以下内容。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_C_INCLUDES := (includes for bullet)
LOCAL_MODULE := bullet
LOCAL_SRC_FILES := libbullet.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_C_INCLUDES := (includes for irrlicht)
LOCAL_MODULE := irrlicht
LOCAL_SRC_FILES := libirrlicht.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := (includes for bullet + includes for irrlicht + includes for main code)
LOCAL_SRC_FILES := main code src files

LOCAL_MODULE := gamescript

LOCAL_ARM_MODE   := arm
LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -O3 -DANDROID_NDK -DDISABLE_IMPORTGL
LOCAL_LDLIBS := -lOpenSLES -landroid -ldl -llog

LOCAL_SHARED_LIBRARIES := bullet irrlicht

include $(BUILD_SHARED_LIBRARY)

现在,以正确的顺序将所有库添加到您的 java 代码中。

System.loadLibrary("bullet");
System.loadLibrary("irrlicht");
System.loadLibrary("gamescript");

【讨论】:

  • 这效果更好。但是,它仍然拒绝加载,这一次是因为它正在寻找 libzmq.so.1,而不是 libzmq.so,即使 libzmq.so.1 也被复制到目录中(并出现在 apk 中)。跨度>
  • 如果您的库 (libgamescript.so) 或您的子库 (libbullet.so) 正在使用第 3 方库(例如在我的情况下为 libstlport.so),则您需要先从 java 加载 stlport你加载你的其他库。 (首先做 system.loadLibrary("stlport");)
  • 对我来说,那就是 libgnustl_shared.so,我确实首先成功加载了 System.loadLibrary("gnustl_shared")。但是,它仍然声称无法找到我之前成功加载过的库。
  • 答案对我来说在所有情况下都有效。我怀疑您是否遵循相同的顺序。还有一个疑问,你是在为许多架构构建它吗?
  • 请注意:Android NDK 包含的文档${NDK}/doc/PREBUILTS.html 描述了详细信息。
猜你喜欢
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-05
  • 2015-04-06
  • 1970-01-01
相关资源
最近更新 更多