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

时间:2014-02-21 00:31:08

标签: android linker android-ndk java-native-interface shared-libraries

我正在编写一个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 ....现在是什么?

3 个答案:

答案 0 :(得分:13)

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

如果您将support_lib1.so重命名为libsupport_1.sosupport_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)

不确定这是否与您所处的位置完全相同,但这是我对这些事情的了解。

  1. 使每个预建的库都有自己独立的Makefile。 Android.mk中的多个目标往往变得不稳定。悲伤。
  2. 使用$(call import-add-path)$(call import-module)
  3. 添加每个品牌文件
  4. 使用LOCAL_EXPORT_变量系列,从预建的make文件中尽可能多地导出。
  5. 预建共享库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)
    

    这假设预建的库存在像这样的目录结构中

    + SharedProjectFolderName
    +--- Android.mk
    +--- include/
    +-+- libs/$(TARGET_ARCH_ABI)/
      |- libshared_library_name.so
    

    如果你没有为多个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)
    

    我建议您将所有共享库放在一个文件夹中。当您说$(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()加载并将库链接到正在运行的进程中。