I am writing an android app that wants to make JNI calls into a shared library built in using the NDK. The trick is this shared library calls functions provided by OTHER shared libraries. The other shared libraries are C libraries that have been compiled elsewhere.
我正在编写一个android应用程序,希望将JNI调用用于使用NDK构建的共享库。诀窍是这个共享库调用由其他共享库提供的函数。其他共享库是C库,它们已经在其他地方编译过。
Here's what I've tried:
这是我试过:
My Environment: I'm working in Eclipse. I've added native support and have a jni library. In that library I have my code and a \lib directory where I have copied my other .so files.
我的环境:我在Eclipse工作。我添加了本地支持,并有一个jni库。在那个库中,我有我的代码和一个\lib目录,在那里我复制了另外的。so文件。
Attempt #1 Android.mk: Just telling it where the libs are
# 1 Android。马克:只是告诉它,它们在哪里
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)
This builds just fine, but when I try to run I get errors indicating that dlopen(libnative_lib) failed because it couldn't load libsupport_lib1.
这个构建很好,但是当我尝试运行时,会出现一些错误,表明dlopen(libnative_lib)失败了,因为它不能加载libsupport_lib1。
Coming here I found this:
来到这里,我发现:
Can shared library call another shared library?
共享库可以调用另一个共享库吗?
which said that I needed to call load library on all necessary libraries. Great!
也就是说,我需要在所有必要的库上调用load library。太棒了!
Attempt #2 Opening each library first
尝试#2首先打开每个库
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Again, this builds just fine, however when I run I get a new error:
同样,这个构建很好,但是当我运行时,我得到一个新的错误:
couldn't load libsupport_lib1. findLibrary returned null.
无法加载libsupport_lib1。findLibrary返回null。
Now we're getting somewhere. It must not be loading the libraries over to the target.
现在我们已经取得了一些进展。它不能将库加载到目标。
Attempt #3 Copying .so files into project/libs/armeabi
将文件复制到项目/libs/armeabi中。
Didn't work. When Eclipse builds it deleted the files I dropped in there.
没有工作。当Eclipse构建时,它删除了我放在那里的文件。
Attempt #4 Creating a new module for each library
尝试#4为每个库创建一个新模块
So then I found this:
于是我发现:
Android NDK: Link using a pre-compiled static library
Android NDK:使用预编译静态库的链接
It's about static libraries, but maybe I am having a similar problem. The gist is that I need to declare a module for each library. So my new Android.mk looks like this:
它是关于静态库的,但也许我也有类似的问题。要点是我需要为每个库声明一个模块。所以我的新的Android。可看起来像这样:
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)
This builds! Even better, armeabi has the sos now! Even BETTER I get the following messages when I try to run it (telling me that support_lib1 and 2 were opened by LoadLibrary:
这个版本!更好的是,阿梅比现在有sos !当我试着运行它时,我得到了以下消息(告诉我support_lib1和2是由LoadLibrary打开的):
Trying to load lib /data/app-lib/com.example.tst/libsupport_lib1.so added shared lib /data/app-lib/com.example.tst/libsupport_lib1.so no JNI_OnLoad found in /data/app-lib/com.example.tst/libsupport_lib1.so, skipping init
尝试加载lib/ data/app-lib/com.example.tst/libsupport_lib1。所以添加了共享库/数据/app-lib/com.example.tst/libsupport_lib1。所以在/data/app-lib/com.example.tst/libsupport_lib1中找不到JNI_OnLoad。所以,跳过初始化
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
但后来…dlopen失败:无法定位func_that_exists_in_libsupport_lib符号。所以libnative_lib.so引用的
Edit: Attempt 5: Use PREBUILT_SHARED_LIBRARY
尝试5:使用PREBUILT_SHARED_LIBRARY
So I found this: How can i Link prebuilt shared Library to Android NDK project?
因此我发现:如何将预构建的共享库链接到Android NDK项目?
which seems to be exactly what I'm asking. Their answer seems to be 'don't use 'build_shared_library' but instead 'use PREBUILT_SHARED_LIBRARY
这似乎正是我要问的。他们的答案似乎是“不使用‘build_shared_library’,而是‘使用PREBUILT_SHARED_LIBRARY’。
Okay, let's try.
好的,让我们试一试。
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)
Build... fails! The build complains about missing symbols now.
构建……失败了!构建现在抱怨缺少符号。
Edit: Attempt 6: Flatten everything
编辑:尝试6:把一切都变平
So I went back to the prebuilts documentation in the NDK. It says:
所以我回到了NDK的预制文档。它说:
Each prebuilt library must be declared as a single independent module to the build system. Here is a trivial example where we assume that the file "libfoo.so" is located in the same directory than the Android.mk below:
每个预构建库必须声明为构建系统的单个独立模块。这里有一个简单的示例,我们假设文件为“libfoo”。so位于与Android相同的目录中。可如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
Notice that, to declare such a module, you really only need the following:
请注意,要声明这样一个模块,您只需要以下内容:
Give the module a name (here 'foo-prebuilt'). This does not need to correspond to the name of the prebuilt library itself.
给模块一个名称(这里是“foo-prebuilt”)。这并不需要与预构建库本身的名称相对应。
Assign to LOCAL_SRC_FILES the path to the prebuilt library you are providing. As usual, the path is relative to your LOCAL_PATH.
将要提供的预构建库的路径分配给LOCAL_SRC_FILES。与往常一样,路径与您的LOCAL_PATH相关。
Include PREBUILT_SHARED_LIBRARY, instead of BUILD_SHARED_LIBRARY, if you are providing a shared, library. For static ones, use PREBUILT_STATIC_LIBRARY. A prebuilt module does not build anything. However, a copy of your prebuilt shared library will be copied into $PROJECT/obj/local, and another will be copied and stripped into $PROJECT/libs/.
包含PREBUILT_SHARED_LIBRARY,而不是BUILD_SHARED_LIBRARY(如果您提供共享的库)。对于静态类,使用PREBUILT_STATIC_LIBRARY。预构建模块不构建任何东西。但是,预先构建的共享库的副本将被复制到$PROJECT/obj/local中,而另一个将被复制并剥离到$PROJECT/libs/中。
So let's try flattening everything out to match the trivial example. I copied my libraries out of their cozy /lib folder and put them in the jni root. I then did this:
我们试着把所有的东西都压平来匹配这个简单的例子。我从它们的舒适/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)
and... same error. Moreover I'm most definitely NOT seeing library files getting copied to $PROJECT/obj/local.
和…同样的错误。此外,我肯定没有看到库文件被复制到$PROJECT/obj/local。
sooooo.... now what?
如此....现在怎么办呢?
3 个解决方案
#1
13
Your problem is with the naming convention. NDK and Android insist on the shared library names to always begin with lib. Otherwise, the libraries will not be linked properly, and not copied to the libs/armeabi
folder properly, and not installed on the device (copied to /data/data/package/lib
directory properly.
您的问题是命名约定。NDK和Android坚持使用共享库的名称,始终以lib开头,否则,库将不能正确链接,不能正确地复制到libs/armeabi文件夹,也不会在设备上安装(正确地复制到/data/data/package/lib目录)。
If you rename support_lib1.so
to libsupport_1.so
and support_lib2.so
to libsupport_2.so
, and put these two files in jni/lib
directory, then your Attempt #5 will work with minor change:
如果你重命名support_lib1。libsupport_1。所以,support_lib2。libsupport_2。因此,将这两个文件放在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)
BTW, I don't think you need this -L$(SYSROOT)/../usr/lib
.
顺便说一下,我不认为你需要这个-L$(SYSROOT)/../usr/lib。
PS Don't forget to update the Java side, too:
PS别忘了更新Java端:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
#2
3
Not sure if this is exactly where you are at, but here's what I know about these sorts of things.
我不确定这是不是你现在的位置,但这是我对这类事情的了解。
- Make each prebuilt libary its own separate Makefile. Multiple targets in Android.mk tends to get wonky. Sad.
- 使每个预构建的libary具有自己的独立Makefile。Android上的多个目标。mk倾向于变得不稳定。伤心。
- Include each make file using
$(call import-add-path)
and$(call import-module)
- 使用$(调用import-add-path)和$(调用import-module)包含每个make文件
- Export as much as you can from the prebuilt's make files, using the
LOCAL_EXPORT_
family of variables. - 使用LOCAL_EXPORT_变量族从预构建的make文件中尽可能地导出。
Prebuilt Shared Library Android.mk
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)
This is assuming that the prebuilt libaries live in a dir structure like this
这是假设预构建的libaries生活在这样的dir结构中。
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
If you are not building for multiple ABI, I guess you can leave that bit out
如果您不是为多个ABI构建,我想您可以省略这个部分
The Project's Android.mk
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)
I recommend you put all shared libraries in one folder. When you say $(call import-module,SharedProjectFolderName)
it looks for a folder containing an Android.mk
along the search path you told it (import-add-path
)
我建议您将所有共享库放在一个文件夹中。当您输入$(调用import-module,SharedProjectFolderName)时,它会查找一个包含Android的文件夹。沿着你告诉它的搜索路径(导入-加载路径)
By the way, you probably shouldn't specify LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib
. It should be finding the proper libs from NDK by itself. Adding more linker paths will probably confuse it. The proper way is to export the linker paths as flags from the sub-modules.
顺便说一下,您可能不应该指定LOCAL_LDLIBS:= -L$(SYSROOT)/../usr/lib。它应该自己从NDK中找到合适的libs。添加更多链接器路径可能会使它混淆。正确的方法是将链接器路径作为子模块的标志导出。
ALSO, you can use ndk-build V=1
to get a ton of info on why it can't find paths, etc
此外,您可以使用ndk-build V=1获取大量关于它为什么找不到路径的信息,等等
#3
0
The -L option gives the linker a directory path in which to look for libraries. The -l option gives the linker a library file name to link in. Library file names must begin with "lib". Your libraries should be named libsupport_lib1.so and libsupport_lib2.so. If you do that, then this is probably what you should do (replacing attempt #1):
l选项为链接器提供了查找库的目录路径。l选项为链接器提供一个要链接的库文件名。库文件名必须以“lib”开头。您的库应该命名为libsupport_lib1。所以,libsupport_lib2.so。如果你这样做了,那么这可能就是你应该做的(替换尝试#1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
The linker will prefix the library name you specify using -l with "lib" and suffix it with ".so". (Why do you have -L$(SYSROOT)/../usr/lib?)
链接器将在您使用-l指定的库名前面加上“lib”,并在其后面加上“so”。(为什么你有-L$(SYSROOT)/.. ./usr/lib?)
I believe that attempts #1 and #2 failed because you did not link your libraries into your executable - they are not mentioned in a -l option. By the way, you can verify this yourself. Unzip the .apk file and look in the lib directory and subdirectories. Are your .so files in there?
我认为尝试#1和#2失败了,因为您没有将库链接到可执行文件中——在-l选项中没有提到它们。顺便说一下,你可以自己验证一下。解压缩.apk文件并查看lib目录和子目录。你的文件在里面吗?
Looking at the error:
查看错误:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
Can you supply the entire message? dlopen() loads and links libraries into the running process.
你能提供整个消息吗?dlopen()加载并将库链接到正在运行的进程中。
#1
13
Your problem is with the naming convention. NDK and Android insist on the shared library names to always begin with lib. Otherwise, the libraries will not be linked properly, and not copied to the libs/armeabi
folder properly, and not installed on the device (copied to /data/data/package/lib
directory properly.
您的问题是命名约定。NDK和Android坚持使用共享库的名称,始终以lib开头,否则,库将不能正确链接,不能正确地复制到libs/armeabi文件夹,也不会在设备上安装(正确地复制到/data/data/package/lib目录)。
If you rename support_lib1.so
to libsupport_1.so
and support_lib2.so
to libsupport_2.so
, and put these two files in jni/lib
directory, then your Attempt #5 will work with minor change:
如果你重命名support_lib1。libsupport_1。所以,support_lib2。libsupport_2。因此,将这两个文件放在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)
BTW, I don't think you need this -L$(SYSROOT)/../usr/lib
.
顺便说一下,我不认为你需要这个-L$(SYSROOT)/../usr/lib。
PS Don't forget to update the Java side, too:
PS别忘了更新Java端:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
#2
3
Not sure if this is exactly where you are at, but here's what I know about these sorts of things.
我不确定这是不是你现在的位置,但这是我对这类事情的了解。
- Make each prebuilt libary its own separate Makefile. Multiple targets in Android.mk tends to get wonky. Sad.
- 使每个预构建的libary具有自己的独立Makefile。Android上的多个目标。mk倾向于变得不稳定。伤心。
- Include each make file using
$(call import-add-path)
and$(call import-module)
- 使用$(调用import-add-path)和$(调用import-module)包含每个make文件
- Export as much as you can from the prebuilt's make files, using the
LOCAL_EXPORT_
family of variables. - 使用LOCAL_EXPORT_变量族从预构建的make文件中尽可能地导出。
Prebuilt Shared Library Android.mk
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)
This is assuming that the prebuilt libaries live in a dir structure like this
这是假设预构建的libaries生活在这样的dir结构中。
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
If you are not building for multiple ABI, I guess you can leave that bit out
如果您不是为多个ABI构建,我想您可以省略这个部分
The Project's Android.mk
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)
I recommend you put all shared libraries in one folder. When you say $(call import-module,SharedProjectFolderName)
it looks for a folder containing an Android.mk
along the search path you told it (import-add-path
)
我建议您将所有共享库放在一个文件夹中。当您输入$(调用import-module,SharedProjectFolderName)时,它会查找一个包含Android的文件夹。沿着你告诉它的搜索路径(导入-加载路径)
By the way, you probably shouldn't specify LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib
. It should be finding the proper libs from NDK by itself. Adding more linker paths will probably confuse it. The proper way is to export the linker paths as flags from the sub-modules.
顺便说一下,您可能不应该指定LOCAL_LDLIBS:= -L$(SYSROOT)/../usr/lib。它应该自己从NDK中找到合适的libs。添加更多链接器路径可能会使它混淆。正确的方法是将链接器路径作为子模块的标志导出。
ALSO, you can use ndk-build V=1
to get a ton of info on why it can't find paths, etc
此外,您可以使用ndk-build V=1获取大量关于它为什么找不到路径的信息,等等
#3
0
The -L option gives the linker a directory path in which to look for libraries. The -l option gives the linker a library file name to link in. Library file names must begin with "lib". Your libraries should be named libsupport_lib1.so and libsupport_lib2.so. If you do that, then this is probably what you should do (replacing attempt #1):
l选项为链接器提供了查找库的目录路径。l选项为链接器提供一个要链接的库文件名。库文件名必须以“lib”开头。您的库应该命名为libsupport_lib1。所以,libsupport_lib2.so。如果你这样做了,那么这可能就是你应该做的(替换尝试#1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
The linker will prefix the library name you specify using -l with "lib" and suffix it with ".so". (Why do you have -L$(SYSROOT)/../usr/lib?)
链接器将在您使用-l指定的库名前面加上“lib”,并在其后面加上“so”。(为什么你有-L$(SYSROOT)/.. ./usr/lib?)
I believe that attempts #1 and #2 failed because you did not link your libraries into your executable - they are not mentioned in a -l option. By the way, you can verify this yourself. Unzip the .apk file and look in the lib directory and subdirectories. Are your .so files in there?
我认为尝试#1和#2失败了,因为您没有将库链接到可执行文件中——在-l选项中没有提到它们。顺便说一下,你可以自己验证一下。解压缩.apk文件并查看lib目录和子目录。你的文件在里面吗?
Looking at the error:
查看错误:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
Can you supply the entire message? dlopen() loads and links libraries into the running process.
你能提供整个消息吗?dlopen()加载并将库链接到正在运行的进程中。