常见问题和解决方案

本文档包含您在使用 NDK 时可能遇到的部分最常见的非 bug 问题及解决方案(如果有)。

_FILE_OFFSET_BITS=64 与旧版 API 级别搭配使用

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

r16 博文bionic 文档对该问题作了详细解释。

问题:您的 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,因此该函数不可用。

minSdkVersion 的设置高于设备的 API 级别

您使用 NDK 进行构建所依据的 API 级别与 Java 中的 compileSdkVersion 所代表的含义截然不同。NDK API 级别是您的应用支持的最低 API 级别。在 ndk-build 中,这是指 APP_PLATFORM 设置。对于 CMake,这是指 -DANDROID_PLATFORM

系统对函数引用的解析通常发生在库加载时,而不是首次调用时,因此,对于并非始终存在并通过 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_ 的任何符号。

RTTI/异常捕获功能无法跨库边界运行

问题:在跨共享库边界抛出异常或 dynamic_cast 失败时,无法捕获异常。

解决方案:为您的类型添加一个关键函数。关键函数是某个类型的第一个非纯的外联虚拟函数。如需查看示例,请参阅关于问题 533 的讨论。

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

如果某个类型没有关键函数,系统会将其 typeinfo 作为弱符号发出,并在加载库时合并匹配类型信息。如果在可执行文件加载完毕后动态加载库(也就是说通过 dlopenSystem.loadLibrary 加载),加载器可能无法合并已加载库的类型信息。当出现这种情况时,这两种类型就不会被视为等同。

使用不匹配的预构建库

在您的应用中使用预构建库(这些库通常是第三方库)时,需要特别注意。一般来说,请注意以下规则:

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

    如果您的 minSdkVersion 是 16,但使用了依据 21 构建的预构建库,那么生成的应用的最低 API 级别为 21。如果预构建库是静态的,那么对于此规则的违反将会在构建时显示出来,但在预构建共享库运行时才会显示。

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

    由于违反的情况极少,此规则比大多数规则的灵活性略高,但无法保证使用不同 Major 版本的 NDK 构建的库之间的兼容性。C++ ABI 并非稳定版,在过去有过变化。

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

    就 STL 不匹配而言,可以通过小心谨慎地操作来避免由此引起的问题,但最好直接避免该问题。为避免该问题,最好避免在您的应用中使用多个共享库。