Android NDK 链接

4
我正在尝试构建一个调用C++后端的Android应用程序。此后端使用ZeroMQ进行消息传递。根据ZeroMQ指南上的 Android构建页面,我已经构建了一个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符号?


请检查您的库是否实际上已经打包到apk中,方法是将其作为zip文件打开,并检查在设备上安装时是否实际上被提取出来(在模拟器上可能最容易)。此外,考虑运行一个测试,其中包含没有依赖关系的本地库 - 即,获取hello-jni ndk示例中的库。 - Chris Stratton
我通过静态链接zeroMQ,但动态支持c++ stl来解决这个问题。 - user1243488
1个回答

14

我刚学会了如何在Android NDK中编译第二个库并将其链接到我的主库。让我看看我是否对您有用。

以下是我创建第二个库的方法(在我的情况下,我将Bullet物理库和Irrlicht渲染引擎构建为两个单独的库用于我的游戏)。

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中)。 - user1243488
如果你的库(libgamescript.so)或子库(libbullet.so)使用第三方库(如我的情况中的libstlport.so),则需要在加载其他库之前从Java中加载stlport。(首先执行system.loadLibrary("stlport");) - codetiger
对我来说,那就是libgnustl_shared.so,我成功地首先加载了System.loadLibrary("gnustl_shared")。然而,它仍然声称找不到我之前已经成功加载的库。 - user1243488
答案对我来说在所有情况下都有效。我怀疑你是否按照相同的顺序进行操作。还有一个疑问,你是为多个架构构建它吗? - codetiger
只是一条注释:Android NDK 包含的文档 ${NDK}/doc/PREBUILTS.html 描述了细节。 - FooF

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