Android NDK/JNI:构建依赖于其他共享库的共享库

djc*_*535 17 java-native-interface linker android shared-libraries android-ndk

我正在编写一个Android应用程序,希望将JNI调用成为使用NDK构建的共享库.诀窍是这个共享库调用OTHER共享库提供的函数.其他共享库是已在其他地方编译的C库.

这是我尝试过的:

我的环境: 我在Eclipse工作.我添加了原生支持并拥有一个jni库.在那个库中,我有我的代码和\ lib目录,我已经复制了我的其他.so文件.

尝试#1 Android.mk:告诉它libs在哪里

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)
Run Code Online (Sandbox Code Playgroud)

这构建得很好,但是当我尝试运行时,我得到错误,表明dlopen(libnative_lib)失败,因为它无法加载libsupport_lib1.

来到这里我发现了这个:

共享库可以调用另一个共享库吗?

这说我需要在所有必要的库上调用load库.大!

尝试#2首先打开每个库

static {
    System.loadLibrary("support_lib1");
    System.loadLibrary("support_lib2");
    System.loadLibrary("native_lib");
}
Run Code Online (Sandbox Code Playgroud)

再次,这构建得很好,但是当我运行时,我得到一个新的错误:

无法加载libsupport_lib1.findLibrary返回null.

现在我们到了某个地方.它不能将库加载到目标.

尝试#3将.so文件复制到project/libs/armeabi中

没工作.当Eclipse构建时,它删除了我放在那里的文件.

尝试#4为每个库创建一个新模块

那么我发现了这个:

Android NDK:使用预编译的静态库链接

它是关于静态库的,但也许我遇到了类似的问题.要点是我需要为每个库声明一个模块.所以我的新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)
Run Code Online (Sandbox Code Playgroud)

这构建!更好的是,armeabi现在有了sos!甚至更好我尝试运行时收到以下消息(告诉我loadLibrary打开了support_lib1和2:

试图加载LIB /data/app-lib/com.example.tst/libsupport_lib1.so加入共享库/data/app-lib/com.example.tst/libsupport_lib1.so没有JNI_OnLoad在/数据中发现/ APP-LIB/com.example.tst/libsupport_lib1.so,跳过init

但后来... dlopen失败:无法找到libnative_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)
Run Code Online (Sandbox Code Playgroud)

构建......失败!构建现在抱怨缺少符号.

编辑:尝试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)
Run Code Online (Sandbox Code Playgroud)

请注意,要声明此类模块,您实际上只需要以下内容:

给模块一个名字(这里是'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)
Run Code Online (Sandbox Code Playgroud)

和......同样的错误.而且我绝对不会看到库文件被复制到$ PROJECT/obj/local.

sooooo ....现在怎么样?

Ale*_*ohn 13

您的问题在于命名约定.NDK和Android坚持共享库名称始终以lib开头.否则,库将无法正确链接,也不会正确复制到libs/armeabi文件夹中,也不会安装在设备上(/data/data/package/lib正确复制到目录中).

如果重support_lib1.solibsupport_1.so,并support_lib2.solibsupport_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)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我认为你不需要这个-L$(SYSROOT)/../usr/lib.

PS别忘了更新Java方面:

static {
    System.loadLibrary("support_lib1");
    System.loadLibrary("support_lib2");
    System.loadLibrary("native_lib");
}
Run Code Online (Sandbox Code Playgroud)

  • 现在gradle提供实验插件,我们可以配置build.gradle文件中的所有内容.你能提供上面Android.mk文件的build.gradle变种吗? (3认同)

yan*_*ano 5

不确定这是否正是您所处的位置,但这就是我对此类事情的了解。

  1. 使每个预构建的库都有自己单独的 Makefile。Android.mk 中的多个目标往往会变得不稳定。伤心。
  2. 使用$(call import-add-path)和包含每个 make 文件$(call import-module)
  3. LOCAL_EXPORT_使用变量系列从预构建的 make 文件中导出尽可能多的内容。

预构建共享库 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)
Run Code Online (Sandbox Code Playgroud)

这是假设预构建的库位于这样的目录结构中

+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
  |- libshared_library_name.so
Run Code Online (Sandbox Code Playgroud)

如果您不是为多个 ABI 构建,我想您可以忽略这一点

该项目的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)
Run Code Online (Sandbox Code Playgroud)

我建议您将所有共享库放在一个文件夹中。当您说它沿着您告诉它的搜索路径$(call import-module,SharedProjectFolderName)查找包含( )Android.mkimport-add-path

顺便说一句,您可能不应该指定LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib. 它应该自己从 NDK 中找到合适的库。添加更多链接器路径可能会使其混乱。正确的方法是将链接器路径作为标志从子模块导出。

另外,您可以用来ndk-build V=1获取大量有关为什么找不到路径等的信息