本文档列出了您在使用 NDK 时可能会遇到的一些最常见的非 bug 问题及其解决方案(如果可用)。
在旧版 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
而不是 mmap
。mmap64
直到 android-21
才可用。如果您的 minSdkVersion
值低于 21,则 C 库不包含与 _FILE_OFFSET_BITS=64
兼容的 mmap
,因此该函数不可用。
minSdkVersion
设置高于设备 API 级别
您使用 NDK 构建时所针对的 API 级别与 compileSdkVersion
对于 Java 的意义完全不同。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
指针相同时,它们才具有相同的类型。只有当 catch 的 type_info
与抛出的异常匹配时,才能捕获异常。同样的规则也适用于 dynamic_cast
。
当一个类型没有关键函数时,它的 typeinfo
会作为弱符号发出,并在加载库时合并匹配的类型信息。在可执行文件加载后动态加载库时(换句话说,通过 dlopen
或 System.loadLibrary
),加载器可能无法合并已加载库的类型信息。发生这种情况时,这两个类型不被视为相等。
使用不匹配的预构建库
在您的应用中使用预构建库(这些通常是第三方库)需要额外小心。通常,请注意以下规则
最终应用的最低 API 级别是应用所有库的
minSdkVersion
中的最大值。如果您的
minSdkVersion
是 16,但您使用的预构建库是针对 21 构建的,则最终应用的最低 API 级别为 21。如果不遵守此规则,静态预构建库在构建时会显示错误,但共享预构建库可能直到运行时才会显示问题。所有库应使用相同的 NDK 版本生成。
此规则比大多数规则更灵活一些,因为很少发生中断,但不保证使用不同主要版本 NDK 构建的库之间的兼容性。C++ ABI 不稳定,过去曾发生变化。
具有多个共享库的应用必须使用共享 STL。
与不匹配的 STL 一样,如果非常小心,可以避免由此引起的问题,但最好还是避免这个问题。避免此问题的最佳方法是避免在您的应用中使用多个共享库。