我正在编写一个Android应用程序,想要调用使用NDK构建的共享库中的JNI函数。难点在于该共享库会调用由其他共享库提供的函数。这些其他共享库是在别处编译的C库。
以下是我的尝试:
我的环境: 我正在使用Eclipse开发。我添加了本机支持并拥有一个jni库。在该库中,我有自己的代码和一个\lib目录,在那里我复制了其他的.so文件。
尝试 #1 Android.mk:只是告诉它库的位置
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
这个构建过程很顺利,但是当我尝试运行时,出现了错误,指示dlopen(libnative_lib)失败,因为它不能载入libsupport_lib1。
在这里我找到了这个:
Can shared library call another shared library?
其中提到需要对所有必要的库进行load library调用。太好了!
尝试 #2 先打开每个库
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
再次构建是没有问题的,但运行时出现了新的错误:
无法加载libsupport_lib1库。findLibrary返回null。
现在我们有所进展了。目标机可能没有加载库文件。
尝试 #3:将 .so 文件复制到项目/libs/armeabi 目录下
这样不行,在 Eclipse 构建时,会删除我放进去的文件。
尝试 #4:为每个库创建一个新模块
于是我找到了这篇文章:
虽然这是关于静态库的,但我可能遇到了类似的问题。大意是说我需要为每个库声明一个模块。因此我的新 Android.mk 文件看起来像这样:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(BUILD_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(BUILD_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
这个构建成功了!更好的是,armeabi现在有了sos!更好的是当我尝试运行它时,我收到以下消息(告诉我支持库1和2由LoadLibrary打开):
正在尝试加载lib /data/app-lib/com.example.tst/libsupport_lib1.so 添加共享库/data/app-lib/com.example.tst/libsupport_lib1.so 在/data/app-lib/com.example.tst/libsupport_lib1.so中未找到JNI_OnLoad,跳过初始化
但是…… dlopen失败:无法定位由libnative_lib.so引用的libsupport_lib.so中存在的函数func_that_exists_in_libsupport_lib.so符号
编辑:第5次尝试:使用PREBUILT_SHARED_LIBRARY
于是我找到了这个: 如何将预构建的共享库链接到Android NDK项目?
这似乎正是我要问的。他们的答案似乎是“不要使用'build_shared_library'而是使用'PREBUILT_SHARED_LIBRARY'
好的,让我们试试吧。
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
构建失败!现在构建抱怨缺少符号。
编辑:尝试6:压平一切
所以我回到了NDK中的预构建文档。它说:
每个预构建库都必须被声明为一个独立的模块,以便于构建系统。以下是一个微不足道的示例,假设文件“libfoo.so”位于与下面的Android.mk相同的目录中:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
请注意,要声明这样的模块,您实际上只需要以下内容:
为模块命名(这里是“foo-prebuilt”)。这不需要与预构建库本身的名称相对应。
将LOCAL_SRC_FILES分配给您提供的预构建库的路径。与往常一样,路径相对于您的LOCAL_PATH。
如果您提供的是共享库,请使用PREBUILT_SHARED_LIBRARY而不是BUILD_SHARED_LIBRARY。对于静态库,请使用PREBUILT_STATIC_LIBRARY。 预构建模块不会构建任何内容。但是,您的预构建共享库的副本将被复制到$PROJECT/obj/local,并且另一个副本将被复制并剥离到$PROJECT/libs/。
因此,让我们尝试将所有内容展平以匹配简单的示例。我将我的库从舒适的/lib文件夹中复制出来,放在jni根目录下。然后我做了这件事:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
同时出现相同错误。此外,我绝对没有看到库文件被复制到$PROJECT/obj/local。
那现在该怎么办?