Android NDK开发详解之常见问题和解决方案

Android NDK开发详解之常见问题和解决方案

本文档包含您在使用 NDK 时可能遇到的部分最常见的非错误性问题及解决方案(如果有)。
将 _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,因此该函数不可用。
注意: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 不匹配而言,可以通过小心谨慎地操作来避免由此引起的问题,但最好直接避免该问题。为避免该问题,最好避免在您的应用中使用多个共享库。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android NDK开发是指利用NDK(Native Development Kit)将C/C++开发的代码编译成so库,然后通过JNI(Java Native Interface)让Java程序调用。在Android开发中,默认使用的是Android SDK进行Java语言的开发,而对于一些需要使用C/C++的高性能计算、底层操作或跨平台需求的场景,可以使用NDK进行开发。 在Android Studio中进行NDK开发相对于Eclipse来说更加方便,特别是在Android Studio 3.0及以上版本中,配置更加简化,并引入了CMake等工具,使得开发更加便捷。首先要进行NDK开发,需要配置环境,包括导入NDK、LLDB和CMake等工具。可以通过打开Android Studio的SDK Manager,选择SDK Tools,在其中选中相应的工具进行导入。 在项目的build.gradle文件中,可以配置一些NDK相关的参数,例如编译版本、ABI过滤器等。其中,可以通过externalNativeBuild配置CMake的相关设置,包括CMakeLists.txt文件的路径和版本号。此外,在sourceSets.main中还可以设置jniLibs.srcDirs,指定so库的位置。 在进行NDK开发时,可以在jni文件夹中编写C/C++代码,并通过JNI调用相关的函数。通过JNI接口,可以实现Java与C/C++之间的相互调用,从而实现跨语言的开发。 综上所述,Android NDK开发是指利用NDK将C/C++开发的代码编译成so库,并通过JNI实现与Java的相互调用。在Android Studio中进行NDK开发相对方便,可以通过配置环境和相应的参数来进行开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值