将 _FILE_OFFSET_BITS=64
与旧版 API 级别搭配使用
在统一头文件之前,NDK 并不支持 _FILE_OFFSET_BITS=64
。如果您在构建应用时定义了该选项,系统会静默地忽略它。现在,_FILE_OFFSET_BITS=64
选项受统一头文件的支持,但在旧版本的 Android 中,很少有 off_t
API 可用作 off64_t
变体。因此,如果将该功能与旧版 API 级别搭配使用,会导致可用函数减少。
问题:您的 build 请求获得您的 minSdkVersion
中不存在的 API。
解决方案:停用 _FILE_OFFSET_BITS=64
或提高 minSdkVersion
。
未声明或隐式的 mmap
定义
您可能会在 C++ 中看到以下错误:
错误:使用了未声明的标识符“mmap”
或在 C 中看到以下错误:
警告:函数“mmap”的隐式声明在 C99 中无效
使用 _FILE_OFFSET_BITS=64
会指示 C 库使用 mmap64
,而不是 mmap
。在 android-21
之前,无法使用 mmap64
。如果 minSdkVersion
值低于 21,则 C 库不包含与 _FILE_OFFSET_BITS=64
兼容的 mmap
,因此该函数不可用。
注意:mmap
只是此问题的最常见表现。C 库中包含 off_t
参数的任何函数都是如此。
注意:从 r16 Beta 版 2 开始,C 库把 mmap64
作为内联函数提供,以减少此问题的这种表现形式。
minSdkVersion
的设置高于设备的 API 级别
您使用 NDK 进行构建所依据的 API 级别与 Java 中的 compileSdkVersion
所代表的含义截然不同。NDK API 级别是您的应用支持的最低 API 级别。在 ndk-build 中,这是指 APP_PLATFORM
设置。对于 CMake,这是指 -DANDROID_PLATFORM
。
注意:如果您使用的是 externalNativeBuild,系统会自动使用您的 minSdkVersion
。
系统对函数引用的解析通常发生在库加载时,而不是首次调用时,因此,对于并非始终存在并通过 API 级别检查保护对其的使用的 API,您将无法引用。如果 API 被引用,那么此 API 就必须存在。
问题:您的 NDK API 级别高于您的设备支持的 API 级别。
解决方案:将 NDK API 级别 (APP_PLATFORM
) 设置为您的应用支持的最低 Android 版本。
构建系统 | 设置 |
---|---|
ndk-build | APP_PLATFORM |
CMake | ANDROID_PLATFORM |
externalNativeBuild | android.minSdkVersion |
对于其他构建系统,请参阅将 NDK 与其他构建系统配合使用。
找不到 __aeabi
符号
以下消息:
UnsatisfiedLinkError:dlopen 失败:找不到“
__aeabi_memcpy
”符号
是运行时可能出现的错误的一个例子。当您尝试加载原生库时,这些错误会显示在日志中。此符号可以是 __aeabi_*
中的任意一个;其中 __aeabi_memcpy
和 __aeabi_memclr
可能是最常见的。
此问题已记录在问题 126 中
找不到 rand
符号
对于以下错误日志消息:
UnsatisfiedLinkError:dlopen 失败:找不到“
rand
”符号
请参阅这条详细的 Stack Overflow 解答。
未定义对 __atomic_*
的引用
问题:某些 ABI 需要 libatomic
才能为原子操作提供一些实现。
解决方案:在链接时添加 -latomic
。
对于以下错误消息:
错误:未定义对“
__atomic_exchange_4
”的引用
此处的实际符号可能是前缀为 __atomic_
的任何符号。
注意:ndk-build 和 CMake 会替您处理这个问题。对于其他构建系统,您可能需要手动处理。
RTTI/异常捕获功能无法跨库边界运行
问题:在跨共享库边界抛出异常或 dynamic_cast
失败时,无法捕获异常。
解决方案:为您的类型添加一个关键函数。关键函数是某个类型的第一个非纯的外联虚拟函数。如需查看示例,请参阅关于问题 533 的讨论。
C++ ABI 规定,当且仅当两个对象的 type_info
指针相同时,两者才具有相同的类型。只有当捕获的 type_info
与抛出的异常匹配时,系统才会捕获异常。这项规则同样适用于 dynamic_cast
。
如果某个类型没有关键函数,系统会将其 typeinfo
作为弱符号发出,并在加载库时合并匹配类型信息。如果在可执行文件加载完毕后动态加载库(也就是说通过 dlopen
或 System.loadLibrary
加载),加载器可能无法合并已加载库的类型信息。当出现这种情况时,这两种类型就不会被视为等同。
注意:对于非多态类型,类型不能具有关键函数。对于非多态类型,RTTI 是多余的,因为可使用 std::is_same
在编译时确定类型等同性。
使用不匹配的预构建库
在您的应用中使用预构建库(这些库通常是第三方库)时,需要特别注意。一般来说,请注意以下规则:
-
生成的应用的最低 API 级别是应用所有库的最大
minSdkVersion
值。如果您的
minSdkVersion
是 16,但使用了依据 21 构建的预构建库,那么生成的应用的最低 API 级别为 21。如果预构建库是静态的,那么对于此规则的违反将会在构建时显示出来,但在预构建共享库运行时才会显示。 -
应使用相同的 NDK 版本生成所有库。
由于违反的情况极少,此规则比大多数规则的灵活性略高,但无法保证使用不同 Major 版本的 NDK 构建的库之间的兼容性。C++ ABI 并非稳定版,在过去有过变化。
-
具有多个共享库的应用必须使用一个共享 STL。
就 STL 不匹配而言,可以通过小心谨慎地操作来避免由此引起的问题,但最好直接避免该问题。为避免该问题,最好避免在您的应用中使用多个共享库。