文 / Google Android NDK 技术负责人 Dan Albert
最新版本的 Android 原生开发工具包 (NDK) Android NDK r16 Beta 1 现在可以下载了:
https://developer.android.google.cn/ndk/downloads/index.html
也可以通过 Android Studio 在 SDK 管理器中获取此版本。
NDK r16 对我们来说是一个重大的里程碑,因为它是我们准备建议用户开始迁移到 libc++ 的第一个版本!我们会在后面提供更多信息。
我们还更新了 libc++ 及其相关项目,因此,这个版本提升了对 C++1z 的支持。请注意,在 C++1z 成为 C++17 之前,包含的任何内容都有可能发生变化。
您可以在此处查看该版本的版本说明:
https://android.googlesource.com/platform/ndk/+/ndk-release-r16/CHANGELOG.md
libc++ 和 libandroid_support
NDK 有一个名为 libandroid_support 的库,这个库可以向后移植 libc++ 依赖、但旧版本不支持的 libc API。我们至今无法认可 libc++(在 NDK 中实现)的原因仍然是对这个库缺乏信心。r16 的焦点是重新编写了这个库,以获得更高的稳定性。
由于 libandroid_support 现在是一个比较小的库,您的应用的行为应当更接近系统的行为。例如,libandroid_support 之前包含对部分 stdio 的替代实现。尽管一些功能向后移植到 ICS,不过,这也意味着替代实现中的任何错误都会出现在自从您的应用引入错误以来发布的 所有 OS 版本中。
在新版本的 libandroid_support 中,我们已将替代实现移除,因此您在较旧的设备上将无法使用某些功能(几乎是一些没人使用的功能,例如格式字符串中的 %a 支持),不过由于没有这些功能,您使用 libc++ 的应用将变得更小、更可靠。
转到 libc++
所以,为什么您应转到 libc++ 呢?首先,其他 STL 今后将不再受支持。我们一直将 libc++ 用于自 Lollipop 以来的 Android 平台,有一项变化让我们的工程师感到非常兴奋。我们在平台中比在 NDK 中更早地完成了这种过渡,因为我们不需要 libandroid_support,并且可以仅更新 libc。
与 NDK 中当前可用的其他 STL 相比,libc++ 完全支持 C++11、C++14 和大多数的 C++1z!Stlport 自从 2008 年以来就没有更新过了,gnustl(我们对 GNU 的 libstdc++ 的叫法,这是为了避免与 Bionic 的 libstdc++ 混淆,后者不是一种 STL)一直以来都不是很适合 Clang,尤其是在与 和 之类的编译器内建函数紧密关联的标头中,更是如此。
我们很有可能让 libc++ 成为下一个 NDK 版本的默认选择,但是现在,如果您还没用过,可以按照下面的说明操作,选择启用这种库。
像其他 STL 一样,libc++ 同时提供静态库和共享库。使用哪一个取决于您的具体情况,但是如果您的应用中有且仅有一个共享库,tl;dr 将使用静态版本,并在所有其他情况下使用共享版本。
ndk-build
将以下内容添加至您的 Application.mk 文件中:
APP_STL := c++_shared
CMake
调用 CMake 时请粘贴以下内容:
-DANDROID_STL=c++_shared
如果您正在通过 Gradle 使用 CMake,请将以下内容添加至您的 build.gradle 中:
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared"
}
}
独立工具链
在创建独立工具链时,请传递 --stl=libc++。
libandroid_support 的未来
如果您已经阅读我们的路线图:
https://android.googlesource.com/platform/ndk/+/master/docs/Roadmap.md
想必了解我们计划扩展 libandroid_support 以向后移植尽可能多的 libc/libm。每次跟大家说到这个问题,我们最多只是收到冷淡的回应。考虑到大家看起来不感兴趣,而且这样做也会让库增大(进而导致 APK 增大,这是每个人都非常感兴趣的话题),我们不再打算扩展。
如果我们误解了您的回应,或者您还没有回应我们,并且希望我们扩展,请告诉我们:
https://github.com/android-ndk/ndk/issues/456
_FILE_OFFSET_BITS=64
tl;dr:如果您想要保留旧 NDK 中的行为,请不要设置 _FILE_OFFSET_BITS=64。
在 NDK 中设置 _FILE_OFFSET_BITS=64 一直都没什么用。此功能在弃用的标头中已经移除。对于统一标头,NDK 现在具有支持此功能的最新标头。
您可以在您的应用中定义 _FILE_OFFSET_BITS=64 宏,以便在 32 位代码中获取对 64 位 off_t 的支持。将 off_t 设为 64 位(默认情况下,它在 32 位代码中为 32 位)和使用 lseek64 调用隐式替换对 lseek 之类 API 的调用可以实现这种支持。
之前版本的 Android 没有添加对 _FILE_OFFSET_BITS=64 的支持。lseek64 这个 API 一直在 bionic 中。大多数 API 在 Lollipop 中添加,其他的一些 API 则到后期版本才添加。
如果您针对的版本不支持正在使用的某个函数的 64 位 off_t 变体,并且已设置 _FILE_OFFSET_BITS=64,该函数将不可用。这与 r15 和 r15b 的行为不同(但与 r15c 的行为一致),在这两个版本中,函数使用 32 位 off_t 错误公开,将被静默截断。
请注意,即使没有不同名称的 _FILE_OFFSET_BITS=64,64 位 off_t API 仍然可用。例如,请调用 lseek64,而不是 lseek。使用 off64_t,而不是 off_t。
最后,由于此功能对具有统一标头的 NDK 来说是一项新功能,如果您只想返回到统一前的标头行为,您要做的只是停止设置 _FILE_OFFSET_BITS=64。
如需了解有关 bionic 中 off_t ABI 详细信息的更多信息,请参阅 Bionic 32 位 ABI 错误文档:
https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md