常见问题及解决方案

本文档列出了使用 NDK 时可能遇到的最常见非错误问题,以及相应的解决方案(如果可用)。

在较旧的 API 级别使用 _FILE_OFFSET_BITS=64

统一头文件 之前,NDK 不支持 _FILE_OFFSET_BITS=64。如果在构建应用时定义了它,则会静默忽略。现在,_FILE_OFFSET_BITS=64 选项已在统一头文件中得到支持,但在旧版本的 Android 上,很少有 off_t API 可作为 off64_t 变体使用。因此,在旧 API 级别使用此功能会导致可用函数减少。

此问题在 r16 博客文章bionic 文档 中进行了详细说明。

问题:您的构建正在请求 minSdkVersion 中不存在的 API。

解决方案:禁用 _FILE_OFFSET_BITS=64 或提高您的 minSdkVersion

未声明或隐式定义 mmap

您可能会在 C++ 中看到以下错误

error: use of undeclared identifier 'mmap'

或在 C 中看到以下错误

warning: implicit declaration of function 'mmap' is invalid in C99

使用 _FILE_OFFSET_BITS=64 指示 C 库使用 mmap64 而不是 mmapmmap64 直到 android-21 才可用。如果您的 minSdkVersion 值低于 21,则 C 库不包含与 _FILE_OFFSET_BITS=64 兼容的 mmap,因此该函数不可用。

minSdkVersion 设置高于设备 API 级别

您使用 NDK 构建的目标 API 级别与 Java 的 compileSdkVersion 的含义大不相同。NDK API 级别是应用的最低支持 API 级别。在 ndk-build 中,这是您的 APP_PLATFORM 设置。在 CMake 中,这是 -DANDROID_PLATFORM

由于对函数的引用通常是在加载库时而不是在首次调用时解析的,因此您无法引用并非始终存在的 API 并使用 API 级别检查来保护其使用。如果引用了它们,则它们必须存在。

问题:您的 NDK API 级别高于设备支持的 API。

解决方案:将您的 NDK API 级别(APP_PLATFORM)设置为应用支持的 Android 的最低版本。

构建系统 设置
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

有关其他构建系统的说明,请参阅在其他构建系统中使用 NDK

无法找到 __aeabi 符号

以下消息

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memcpy"

是一个可能的运行时错误示例。当您尝试加载本机库时,这些错误会出现在日志中。该符号可能是任何 __aeabi_*__aeabi_memcpy__aeabi_memclr 似乎是最常见的。

此问题在问题 126中进行了记录

无法找到符号 rand

对于以下错误日志消息

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand"

请参阅此详细的Stack Overflow 答案

__atomic_* 的未定义引用

问题:某些 ABI 需要 libatomic 来提供一些原子操作的实现。

解决方案:链接时添加 -latomic

对于以下错误消息

error: undefined reference to '__atomic_exchange_4'

此处的实际符号可能是任何以 __atomic_ 为前缀的符号。

RTTI/异常在库边界之间无法工作

问题:当在共享库边界之间抛出异常时,不会捕获异常,或者 dynamic_cast 失败。

解决方案:向您的类型添加关键函数。关键函数是类型的第一个非纯、非内联虚函数。例如,请参阅有关问题 533的讨论。

C++ ABI 指出,当且仅当两个对象的 type_info 指针相同,这两个对象才具有相同的类型。只有当捕获的 type_info 与抛出的异常匹配时,才能捕获异常。相同的规则适用于 dynamic_cast

当类型没有关键函数时,其 typeinfo 会作为弱符号发出,并且在加载库时会合并匹配的 typeinfo。当在可执行文件加载后动态加载库时(换句话说,通过 dlopenSystem.loadLibrary),加载程序可能无法合并已加载库的 typeinfo。发生这种情况时,这两个类型不会被视为相等。

使用不匹配的预构建库

在您的应用程序中使用预构建库(这些通常是第三方库)需要格外小心。一般来说,请注意以下规则

  • 生成的应用程序的最低 API 级别是所有应用程序库的 minSdkVersion 的最大值。

    如果您的 minSdkVersion 为 16,但您使用的是针对 21 构建的预构建库,则生成的应用程序的最低 API 级别为 21。如果预构建库是静态的,则在构建时会看到不遵守此规则的情况,但对于预构建的共享库,可能要等到运行时才会出现。

  • 所有库都应使用相同的 NDK 版本生成。

    此规则比大多数规则都更灵活一些,因为很少出现故障,但不能保证使用不同主要版本的 NDK 构建的库之间的兼容性。C++ ABI 不稳定,并且过去曾发生过更改。

  • 具有多个共享库的应用程序必须使用共享 STL

    与不匹配的 STL 一样,如果非常小心,可以避免此问题导致的问题,但最好是避免此问题。避免此问题的最佳方法是避免在应用程序中使用多个共享库。