我正在编写一个Android应用程序,希望将JNI调用转换为使用NDK构建的共享库。诀窍是这个共享库调用OTHER共享库提供的函数。其他共享库是已在其他地方编译的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库。太好了!
尝试#2首先打开每个库
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
同样,这构建得很好,但是当我运行时,我得到一个新的错误:
无法加载libsupport_lib1。 findLibrary返回null。
现在我们到了某个地方。它不能将库加载到目标。
尝试#3将.so文件复制到project / libs / armeabi
没用。当Eclipse构建时,它删除了我放在那里的文件。
尝试#4为每个库创建一个新模块
然后我发现了这个:
Android NDK: Link using a pre-compiled static library
这是关于静态库,但也许我遇到了类似的问题。要点是我需要为每个库声明一个模块。所以我的新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!甚至 BETTER 当我尝试运行它时,我收到以下消息(告诉我loadLibrary打开了support_lib1和2:
尝试加载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,跳过init
但是...... dlopen失败:找不到libnative_lib.so引用的符号func_that_exists_in_libsupport_lib.so
编辑:尝试5:使用PREBUILT_SHARED_LIBRARY
所以我发现了这个: How can i Link prebuilt shared Library to Android NDK project?
这似乎正是我所要求的。他们的答案似乎是“不要使用'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中的prebuilts文档。它说:
必须将每个预构建的库声明为构建系统的单个独立模块。这是一个简单的例子,我们假设文件“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 /中。
因此,让我们尝试将所有内容展平以匹配琐碎的例子。我将我的库复制到他们的comfort / 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。
sooooo ....现在是什么?
答案 0 :(得分:13)
您的问题在于命名约定。 NDK和Android坚持共享库名称始终以 lib 开头。否则,库将无法正确链接,也不会正确复制到libs/armeabi
文件夹,也不会安装在设备上(正确复制到/data/data/package/lib
目录。
如果您将support_lib1.so
重命名为libsupport_1.so
而support_lib2.so
重命名为libsupport_2.so
,并将这两个文件放在jni/lib
目录中,那么您的尝试# 5 可以进行微小改动:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := lib/libsupport_1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := lib/libsupport_2.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)
顺便说一句,我认为你不需要这个-L$(SYSROOT)/../usr/lib
。
PS 不要忘记更新Java端:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
答案 1 :(得分:4)
不确定这是否与您所处的位置完全相同,但这是我对这些事情的了解。
$(call import-add-path)
和$(call import-module)
LOCAL_EXPORT_
变量系列,从预建的make文件中尽可能多地导出。LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_module_name
MY_LIBRARY_NAME := shared_library_name
### export include path
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
### path to library
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/lib$(MY_LIBRARY_NAME).so
### export dependency on the library
LOCAL_EXPORT_LDLIBS := -L$(LOCAL_PATH)/libs/$(TARGET_ARCH_ABI)/
LOCAL_EXPORT_LDLIBS += -l$(MY_LIBRARY_NAME)
include $(PREBUILT_SHARED_LIBRARY)
这假设预建的库存在像这样的目录结构中
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
如果你没有为多个ABI构建,我猜你可以把这个留下来
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_jni_module
## source files here, etc...
### define dependency on the other library
LOCAL_SHARED_LIBRARIES := my_module_name
include $(BUILD_SHARED_LIBRARY)
$(call import-add-path,$(LOCAL_PATH)/path/to/myLibraries/)
$(call import-module,SharedProjectFolderName)
$(call import-module,AnotherSharedProject)
我建议您将所有共享库放在一个文件夹中。当您说$(call import-module,SharedProjectFolderName)
时,它会在您告诉它的搜索路径中查找包含Android.mk
的文件夹(import-add-path
)
顺便说一下,你可能不应该指定LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib
。应该从NDK中找到合适的库。添加更多链接器路径可能会使其混淆。正确的方法是将链接器路径作为标志从子模块中导出。
此外,您可以使用ndk-build V=1
获取有关其无法找到路径等的大量信息
答案 2 :(得分:0)
-L选项为链接器提供了一个查找库的目录路径。 -l选项为链接器提供要链接的库文件名。库文件名必须以" lib"开头。您的库应命名为libsupport_lib1.so和libsupport_lib2.so。如果你这样做,那么这可能是你应该做的(替换尝试#1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
链接器将使用-l指定您指定的库名称前缀为" lib"并以" .so"为后缀。 (为什么你有-L $(SYSROOT)/../ usr / lib?)
我认为尝试#1和#2失败是因为您没有将库链接到可执行文件中 - 它们未在-l选项中提及。顺便说一句,您可以自己验证。解压缩.apk文件并查看lib目录和子目录。你的.so文件在那里吗?
查看错误:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
你能提供整个讯息吗? dlopen()加载并将库链接到正在运行的进程中。