【问题标题】:Loading a shared library with the System.loadLibrary call never returns使用 System.loadLibrary 调用加载共享库永远不会返回
【发布时间】:2014-02-20 17:30:30
【问题描述】:

我在使用 System.loadLibrary("my_shared_lib"); 加载本机库时遇到问题 问题是这个调用永远不会返回。

这里是上下文:

在我的项目中,我有几个使用 nkd-build 脚本构建的静态库。 为我需要的每个 .a 库使用此 Android.mk 构建它们效果很好:

**Android.mk used for static libs**

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my_static_lib_1

# Include paths
LOCAL_C_INCLUDES := \
    $(PATH_TO_INCLUDES1) \
    $(PATH_TO_INCLUDES2)

# Sources
LOCAL_SRC_FILES := \
    my_source_1.cpp\
    my_source_11.cpp


# Target
include $(BUILD_STATIC_LIBRARY)

这些静态库当然只包含本机 (C++) 源代码,以及一些 JNI 包装器(可从 Java 源代码调用)。

一旦构建了所有静态库 (.a),我想构建一个共享库 (.so),其中包含我需要的所有 .a 库。 这是我用来构建这个共享库的Android.mk

LOCAL_PATH := $(call my-dir)
#----------
# Static prebuilt libs 
#----------
#-- my_static_lib_1
include $(CLEAR_VARS)
LOCAL_MODULE    := my_static_lib_1
LOCAL_SRC_FILES := $(PATH_TO_LIBS)/my_static_lib_1.a
include $(PREBUILT_STATIC_LIBRARY)

#-- my_static_lib_2
include $(CLEAR_VARS)
LOCAL_MODULE    := my_static_lib_2
LOCAL_SRC_FILES := $(PATH_TO_LIBS)/my_static_lib_2.a
include $(PREBUILT_STATIC_LIBRARY)

#----------
# Building shared lib
#----------
include $(CLEAR_VARS)
LOCAL_MODULE := my_shared_lib

#-- Some of the .a static libs need these libs
LOCAL_LDLIBS := \
    -lz\
    -llog \
    $(PATH_TO_NDK_LIBS)\libstlport_static.a

#-- This variable is used to force libs to be included in the .so
LOCAL_WHOLE_STATIC_LIBRARIES := \
    my_static_lib_1\
    my_static_lib_2

include $(BUILD_SHARED_LIBRARY)

这为我构建了一个完美的 .so 文件,其中包含来自 .a 库的所有函数(我可以使用生成的 .so 库上的 nm 命令来检查)。

为了完整,这里是动态加载.so库的.java文件:

public class MyClass
{
    static {
        Log.d("MYLOGS", "Loading...");
        System.loadLibrary("my_shared_lib");
        Log.d("MYLOGS", "Loaded.");
    }
    ...
}

问题:

当我创建 MyClass 的新实例时,会调用 loadLibrary,但它永远不会返回。我可以看到Loading... 日志,但看不到Loaded. 日志。 logCat 显示 Trying to load lib [...].so 但仅此而已,应用程序冻结了。

当我的.so 文件中只有一个静态库.a 时,所有这些都可以正常工作。在这种情况下,我可以完美地调用我的本机代码。但是我的项目使用了 8 个 .a 文件,并且我得到了一个冻结的应用程序,因为 loadLibrary 在这种情况下永远不会结束。

loadLibrary 调用有什么问题?你有什么想法吗?

谢谢。

【问题讨论】:

  • 你能有一个静态初始化程序或加载类型的函数进入无限循环吗?当“应用程序永远不会返回”时,最终会发生什么?您是否收到“应用程序无响应”对话框? logcat 中的任何内容表明各种线程卡在哪里?您可以尝试用类似使用多个 .a 文件构建的简单库替换真正的库,看看您是否可以缩小问题所在 - 它是有多个文件,还是特别是其中一个文件?
  • 您也可以尝试延迟库加载,以便您有机会先连接 ndk-gdb,然后使用调试器观察加载尝试期间发生的情况。
  • 我没有显示应用程序没有响应的对话框。我有一个黑屏,logcat 说“启动超时已过期,放弃唤醒锁定!”就在“尝试加载 lib.so”之后。 (知道在应用程序启动时调用了loadLibrary。所以看起来它不是死锁,但loadLibrary似乎在等待一些东西......

标签: java android android-ndk java-native-interface loadlibrary


【解决方案1】:

我发现我的代码出了什么问题。

实际上,当 loadLibrary 加载 .so 库时,它会创建在 .so 中声明的所有全局变量/常量,当然包括 .so 中包含的每个 .a 库的每个全局变量。

其中一些全局是通过执行某些代码的构造函数构建的。 我发现在我的.a libs 架构中,这段代码在某些时候会死锁,因为它被调用得太早(一些需要的东西还不存在)。没想到会在loadLibrary时间调用。

因此,如果这可以帮助任何人:请记住,loadLibrary 涉及创建您尝试加载的 .so 库中包含的所有全局对象。

不知道那是我的错误。

【讨论】:

  • 还有 gcc 特定的__attribute__((constructor))__init。如果您想了解它们在加载共享库时如何行为不端,请参阅GCC docsJNI_OnLoad() 将在 所有构造函数完成后调用。
【解决方案2】:

一般来说,当您构建共享库 (.so) 时,会使用一些编译器选项(例如 -fPIC)来生成与位置无关的代码。另一方面,当您构建静态库 (.a) 时,不使用此类选项。即使将静态库打包到共享库中,静态库中的二进制代码也不是位置无关的。我猜这就是 loadLibrary() 失败的原因。

如果我是你,我会避免将静态库打包到共享库中,而是使用 -fPIC 或等效的东西编译所有 C++ 文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-15
    • 2017-12-06
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 2011-12-27
    • 1970-01-01
    相关资源
    最近更新 更多