简介:android-ndk-r25b-darwin.zip 是为 macOS 用户准备的 Android NDK r25b 工具包,面向需要在 Android 工程中嵌入 C/C++ 原生代码的开发者,广泛应用于高性能计算、图形渲染、游戏引擎等场景。它支持 JNI 互调、ndk-build 与 CMake 构建,并提供多线程、内存管理、调试以及 APK 大小优化等开发要点的支持。解压后共 2000 个文件,压缩包约 683.4 MB;其中 1888 个 .h 头文件覆盖 ARM NEON、Vulkan、相机等 API 声明,85 个 .py 脚本可用于构建或调试辅助,另有 md、txt 等文档和 c/cpp 文件帮助理解配置。已有 403 人学习/下载,适合具备一定 C/C++ 基础、希望深入原生层的读者。借助该包可迅速搭建 NDK 开发环境,并在统一的工具链中编译生成 .so 库;头文件与脚本也可作为日常查阅 JNI 声明和自动化构建的参考资料,可有效提升原生模块开发效率。
1. 一份 android-ndk-r25b-darwin.zip,是 JNI 工程绕不开的版本锚点
如果你维护过 2022 到 2023 年间的原生工程、接手过别人拷来的 Android SDK 目录、或者在 CI 服务器上遇到依赖包下载中断的窘境,大概率会在内网盘或迁移包里见到这个文件名:android-ndk-r25b-darwin.zip。它是 NDK r25b 在 macOS 平台上的完整分发归档,涵盖 arm64-v8a、armeabi-v7a、x86、x86_64 四类 ABI 的交叉编译工具链,对应 AGP 自动下载时最常见的版本目录 25.1.8937393。接下来的内容围绕这份 zip 展开:r25b 在 NDK 版本序列中的定位、darwin 包与 Linux/Windows 包的差异、在 macOS 上从解压到产出 .so 的完整过程,以及老工程迁移时最常见的几个坑。
2. r25b 在 NDK 版本线里的真实坐标:AGP 匹配与 darwin 包的边界
2.1 为什么偏偏是 r25b 这个稳定点
NDK r25 正式版发布于 2022 年,r25b 是它的补丁版本。它常被看成“老工艺终点”,原因是这个版本同时满足两件事:工具链已经完全过渡到 clang/gcc 时代彻底翻篇,但最低支持 API level 仍然保留着 32 位 ABI 可到 16 的旧口径。往后的 r26 把全 ABI 最低支持提升到 21,很多还挂着 minSdkVersion 19 的存量项目就很难直接切过去。
此外,大量 AGP 7.3.x 到 8.0.x 工程默认的 NDK 版本写的就是 25.1.8937393。gradle 自动下载时会在本地 SDK 的 ndk/ 目录下创建同名文件夹,本质上就是把这颗 zip 解压后的结构原样放好。离线环境或内网镜像场景里,手动解开这份 darwin.zip 再放到对应目录,比让 gradle 反复重试下载要省事得多。
提示:r25b 不是 NDK 目录里“越大越新”的版本,它在 r24 与 r26 之间承担的是过渡稳定位。2023 年之前发布的企业级 SDK、金融与 IoT 厂商的 so 库,多数编译基线就是它。
2.2 用版本对照表确认兼容边界
以下数据按各 release note 常见口径整理,实际操作时以你的 AGP 与 SDK 组合为准:
| NDK 版本 | 目录版本号 | 默认编译器 | 32 位 ABI 最低 API | 64 位 ABI 最低 API | 常见 AGP 搭配 |
|---|---|---|---|---|---|
| r23b | 23.1.7779620 | clang 12 | 16 | 21 | AGP 7.1/7.2 |
| r24 | 24.0.8215888 | clang 14 | 16 | 21 | 不常见 |
| r25b | 25.1.8937393 | clang 14.0.7 | 16 | 21 | AGP 7.3/7.4/8.0 |
| r26b | 26.1.10909125 | clang 16 | 21 | 21 | AGP 8.2+ |
表中“最低 API”指编译链接时允许指定的最低 android- 平台级别。比如项目 build.gradle 里配了minSdk 19,在 r26b 下编译就会遇到平台级别过低的报错,而 r25b 可以正常工作。反过来,如果你今天新建工程且没有历史包袱,直接选 r26b 或更新版本即可,没必要抱着 darwin.zip 不放。
2.3 darwin 包的边界不止“Mac 可用”这么简单
darwin 是 macOS 内核名,这份 zip 解压后的工具链目录为 toolchains/llvm/prebuilt/darwin-x86_64。注意目录名里的 x86_64:Intel Mac 可以直接执行;Apple Silicon 上则依赖 Rosetta 2 转译,首次执行 clang 时系统会提示安装对应运行环境,之后编译速度比原生 arm64 工具链略慢,但产物没有差别。
常见的误用是把 darwin 包在 Linux 节点上解压,然后 clang 报 Exec format error;或者反过来把 linux 包拖到 mac 上跑。排查这类问题先看压缩包平台后缀与uname -m,再去看工具链 prebuilt 目录名,两个信息一对,基本不会找错方向。
3. macOS 上从 zip 到可用 NDK:解压、校验与三项配置
3.1 按 SDK 规范解压并归位
Android SDK 对 NDK 的目录约定是$ANDROID_SDK_ROOT/ndk/<版本号>/,r25b 对应的版本号是 25.1.8937393。只要解压后的目录名与 ndkVersion 对得上,AGP 就能自动发现,无需额外写死绝对路径。
# 请按本机实际 SDK 位置修改 ANDROID_SDK_ROOT="$HOME/Library/Android/sdk" mkdir -p "$ANDROID_SDK_ROOT/ndk" unzip -q android-ndk-r25b-darwin.zip mv android-ndk-r25b "$ANDROID_SDK_ROOT/ndk/25.1.8937393"这里先建 ndk 根目录,再解压出 android-ndk-r25b 文件夹,最后重命名为版本号形式。用 mv 而不是直接解压到目标目录,是为了保证 zip 内的根目录名与 AGP 期望的版本目录一致,避免后续因目录名不匹配而触发“NDK not found”。
3.2 环境变量、local.properties 与自检命令
命令行编译时建议把下面两行写进 ~/.zshrc 或 CI 脚本顶部,这是 macOS 上手工调 NDK 的最小环境配置:
export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/25.1.8937393" export ANDROID_NDK_ROOT="$ANDROID_SDK_ROOT/ndk/25.1.8937393"第一行是新工具链默认识别的变量,第二行兼容老构建体系。随后可以做一次快速自检,确认 clang 能正常加载目标平台:
"$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang" \ --target=aarch64-linux-android21 \ --sysroot="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/sysroot" \ -v 2>&1 | head -8--target=aarch64-linux-android21明确目标 ABI 与 API level 21,--sysroot指向 NDK 自带的系统头文件与链接库。看到 clang 版本和 ld 路径正常输出,说明工具链整体可用。对于 gradle 工程,则需要在local.properties写sdk.dir=...,并在模块级 build.gradle 中声明ndkVersion = "25.1.8937393"。
提示:AGP 7.0 起已标记
ndk.dir为废弃用法。交接老仓库时若看到 local.properties 里写了 ndk.dir,建议迁移成 ndkVersion 声明,否则后续升级 AGP 会先在这里出警告。
3.3 校验 zip 完整性,别等编译到一半才发现缺文件
网络下载的 zip 或内网拷贝的归档,建议先做一次哈希校验,对照 NDK 发布页给出的 sha256 值:
shasum -a 256 android-ndk-r25b-darwin.zip手头没有官方哈希时,还有一个更快的完整性动作:直接跑ndk-build --version。
"$ANDROID_NDK_HOME/ndk-build" --version这条命令会触发 NDK 加载工具链描述文件与预置脚本,核心文件缺失时会在第一次运行就报出具体路径。先哈希后命令,两份验证都过了,这份 zip 才算真正变成可用 NDK。
4. 用 r25b 出产物:ndk-build 与 CMake 两条常用路径
4.1 旧式工程:Android.mk 与 ndk-build 的最小组合
老一批 SDK 仓库还保留着 jni/Android.mk 的写法,这类工程不需要 gradle 介入,直接用 r25b 自带的 ndk-build 脚本即可。先准备一个最小的 Android.mk:
# jni/Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hellojni LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY)然后执行编译命令:
"$ANDROID_NDK_HOME/ndk-build" \ APP_ABI=arm64-v8a,armeabi-v7a \ APP_PLATFORM=android-21 \ -j 8APP_ABI用逗号分隔,不写默认编全部四种 ABI;APP_PLATFORM决定头文件与链接库的 API level,android-21 是既能覆盖 64 位设备又不过度放宽的常用档位。-j 8是并行任务数,按 CPU 核数调整。产物会落在libs/<abi>/libhellojni.so。想查看完整编译命令加V=1,清理旧产物执行ndk-build clean。
4.2 现代工程:gradle 调起 CMake 的推荐配置
现在更多工程走 CMake 路径。build.gradle 里需要同时声明 NDK 版本和 CMake 参数:
android { ndkVersion = "25.1.8937393" defaultConfig { externalNativeBuild { cmake { cppFlags "-std=c++17" // 用 shared 版 STL,避免多个 so 各自静态链接一份 libc++ 导致符号冲突 arguments "-DANDROID_STL=c++_shared" } } } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" } } }对应的最小 CMakeLists.txt:
cmake_minimum_required(VERSION 3.22.1) project(hellojni) add_library(hellojni SHARED hello.cpp) # 链接系统库:log 用于 __android_log_print,android 用于 JNI 相关 API target_link_libraries(hellojni log android)两处参数需要展开说明。-DANDROID_STL=c++_shared决定 C++ 运行时以同名 libc++_shared.so 打进 APK 而非静态合并进产物;工程里有多个 so 相互调用时选 shared 几乎是必须的,否则各自静态链接后运行时容易出现重复符号或 typeinfo 不一致。target_link_libraries中链接 log 与 android 是 JNI 工程常规操作,前者提供日志输出,后者提供 native 侧访问系统能力的入口。
配置完后直接执行:
./gradlew :app:assembleDebug产物在app/build/intermediates/merged_native_libs/debug/out/lib/下。验证方式就是确认每个 ABI 子目录都有对应 .so,再用上一章的自检命令确认 clang 版本确实来自 r25b 目录。
4.3 常用命令与参数对照
| 目标 | 命令或配置 | 说明 |
|---|---|---|
| 只编指定 ABI | 在 cmake 块加abiFilters "arm64-v8a" | 比注入编译属性更可控,适合 CI |
| 清理 gradle 与 CMake 缓存 | ./gradlew clean | 换 NDK 版本后必须执行一次 |
| 查看 ndk-build 完整日志 | ndk-build V=1 | 定位头文件路径与链接顺序用 |
| 切换 STL 实现 | -DANDROID_STL=c++_shared/c++_static | 单个 so 且无跨 so 符号交互才用 static |
有一点常被忽略:abiFilters和 ndk-build 的APP_ABI不要同时在不同位置写两份来源不同的列表,否则构建系统会以较严格的一侧为效率优先,反而掩盖掉某些 ABI 的真实编译错误。
5. 换个 NDK 就崩的排查路径与 darwin 包实战细节
5.1 三个高频报错的快速定位
| 报错原文(节选) | 常见原因 | 处理方式 |
|---|---|---|
Exec format error | 在 Linux 节点上解压了 darwin 包,或反了 | 换同版本对应平台的 zip,重新配置路径 |
Unsupported option '-faddrsig' | Makefile 里还留着 clang 3.x 时代的参数 | 清理与重定位相关的历史 flags,只留必要优化项 |
No rule to make target ...liblog.so | APP_PLATFORM 指定过低或缺target_link_libraries | 提升 APP_PLATFORM 到 android-21,补 log 依赖 |
这三个问题都不是 r25b 特有,但在老工程换 NDK 时最先炸出来。
5.2 多 NDK 版本共存的切换技巧
把 r25b 和更高版本全部放在$ANDROID_SDK_ROOT/ndk/下,目录间互不干扰。命令行切版本时不要逐个 export,用函数更省事:
use_ndk() { export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/$1" export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME" echo "current: $ANDROID_NDK_HOME" } use_ndk 25.1.8937393切换后补一句ndk-build --version或clang --version确认落点,比靠 echo 输出更可靠。
5.3 一次性确认 so 的真实 ABI
产物出来之后,用 r25b 自带的 llvm-readelf 做最后一道验证:
"$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf" \ -h path/to/libhellojni.so | grep 'Machine'输出里EM_AARCH64对应 arm64-v8a,EM_ARM对应 32 位 armeabi-v7a。搭配llvm-objdump --triple=aarch64-linux-android21 -d反汇编一段函数,还能进一步核对编译目标的 CPU 架构等级。这个组合在团队切换 NDK 版本后做产物对比时非常顺手,不需要依赖真机就可以把版本兼容性问题锁定到单条指令级别。
本文还有配套的精品资源,点击获取