做 Cocos Creator 的 Android 打包,逃不掉的一个关卡就是本地环境的搭建。我自己第一次用 Cocos Creator 出 Android 包时,构建进度条走到一半,控制台直接甩出一行红字:“NDK not configured. Download it with SDK Manager. Preferred NDK version is r23c”。当时我第一反应是去 SDK Manager 里点了个最新版 NDK 下载,装上后继续构建,结果报错更多。后来才发现,NDK、SDK、CMake、JDK、Gradle 这几样东西,每一样都有版本匹配的问题,不是装齐就能跑通。这篇东西就是把我在 Cocos Creator Android 打包过程中关于 NDK 版本选择、配置路径、环境验证、典型报错排查的经验整理出来,给正在被构建环境折腾的人一个能照着操作的参考。
这篇内容适合谁看?刚接触 Cocos Creator 打包 Android APK 的新人,做了两年 H5 小游戏突然要出 Android 包的前端,还有在维护多版本老项目、被 NDK 版本来回切换搞到头秃的客户端同学。文章不会从 Android Studio 怎么下载这种最基础的地方开始讲,但会覆盖从环境角色划分到版本选择逻辑,再到真实报错排查的完整路径。
1. 打包之前先弄明白:Cocos 构建 Android 到底需要哪几件套
很多人在配置环境这一步就卡住,根本原因是没搞清楚这套构建链路里每个工具扮演的角色。我见过不少同事,为了打包把 Android Studio、Eclipse、各种命令行工具装了一堆,最后还是一堆报错,因为根本不知道每个组件是干嘛的、谁调用谁。
1.1 SDK、NDK、JDK、CMake、Gradle 各自扮演什么角色
先说最基础的分工。Cocos Creator 打包 Android,本质上做的事情是:把 JavaScript/TypeScript 写的游戏逻辑打包,和 C++ 引擎源码一起编译成 Android 平台上能跑的 APK 或者 AAB。
这个过程中每个组件的职责是这样的:
Android SDK(Android Software Development Kit):提供 Android 平台的基础开发库和编译工具。包里的
platforms/android-XX是不同 API Level 的 android.jar,打包后的目标系统版本就是靠它决定的。SDK Manager、adb、aapt2 这些工具也在 SDK 里。SDK 是老大,NDK 和 CMake 通常都装在它的目录下面。NDK(Native Development Kit):用于编译 C/C++ 代码的一套工具链。Cocos 引擎核心就是 C++,所以 NDK 是必装的。它包含交叉编译器(clang/gcc)、sysroot(各 Android 平台的头文件和库)、以及
ndk-build等工具。NDK 版本决定了 C++ 标准库实现方式(libc++_shared.so)、最低支持的系统版本等关键属性。JDK(Java Development Kit):编译 Java/Kotlin 代码的工具。Gradle 本身是跑在 JVM 上的,所以 JDK 要先存在,然后才能运行 Gradle,再去调 Android Gradle Plugin 完成 Java 层代码的编译和资源打包。
CMake 与 Ninja:Cocos Creator 3.x 从构建原生库开始就走 CMake 工作流。CMake 生成构建描述,Ninja 是实际执行编译的构建系统。Android Studio 安装 CMake 时一般会连带安装 Ninja,如果没装,就会出现“CMake was unable to find a build program corresponding to 'Ninja'”的报错。
Gradle 与 Android Gradle Plugin(AGP):Gradle 是整个构建流程的调度者。它不是 Cocos 定义的,而是 Android 工程通用方案。Cocos Creator 生成一个 Android 工程(Android Studio 工程风格),然后用 Gradle 把它构建成 APK。AGP 是 Gradle 和 Android SDK 之间的桥梁,不同 AGP 版本也有自己要求的 Gradle 最低版本和 JDK 版本。
1.2 Cocos 2.x 与 3.x 两代构建链路差异
Cocos Creator 2.x 时代的构建链相对陈旧,依赖的是ANT这个自动化构建工具,加上 NDK_ROOT、ANDROID_SDK_ROOT、ANT_ROOT 三个环境变量。2.x 项目的 C++ 代码用 ndk-build 来编译,构建脚本相对简单,但对环境变量的依赖非常深,一不小心就给你报个NDK_ROOT not defined。
Cocos Creator 3.x 重构了构建链路。3.0 开始默认使用CMake + Ninja + Gradle组合。构建的时候,Cocos 的构建插件会读取你配置的 Android SDK 路径,传参给 Gradle,Gradle 再调 CMake 编译原生库。这个链路里,Cocos 对 NDK 的定位方式变了:它倾向于在 SDK 目录下寻找指定版本的 NDK,而不是通过一个独立的 NDK_ROOT 环境变量。
理解这一点对后面的配置非常关键。如果你之前搜教程时看到有人说“去系统环境变量里设置 NDK_ROOT”,那是 2.x 的老经验,直接套用到 3.8 上很可能不生效,甚至会造成困扰。
1.3 为什么装了“最新 NDK”反而更容易出问题
这是我见过的最普遍的认知误区,包括我自己第一次也踩了。Android SDK Manager 默认只显示最新版 NDK,很多人顺手就装了这个最新版,然后构建失败。
核心原因在于:Cocos 引擎在编译 C++ 代码时,内部有一批预编译脚本和编译参数是基于特定 NDK 版本调校的。NDK 每个大版本之间,编译器默认 C++ 标准、内置的 sysroot 路径、libc++ 的实现细节都会有变化。
举一个我实际遇到过的情况:Cocos Creator 3.8.x 的推荐 NDK 是 r23c,但如果你装了 NDK r25 或 r26,构建时 clang 版本从 14 升到 17,引擎源码里某些写法在新的编译参数下会触发更严格的警告,甚至可以因为警告被当成错误直接中断。另一类是运行期问题:r25 之后移除了部分旧 ABI 支持,打包后的 so 文件在旧设备上直接加载不了。
所以我给一句非常直白的话:在 Cocos Creator 的 Android 打包场景里,“最新”从来不是选择标准,“匹配”才是。
2. NDK 版本怎么选:别盯最新版,盯引擎的推荐
讲完整体的工具链,现在重点落到 NDK 本身。NDK 版本选择这件事,说复杂很复杂,说简单也简单:核心思路是找到你使用的 Cocos Creator 版本在构建脚本里指定的“期望 NDK 版本”,然后照着装。
2.1 “Preferred NDK version” 到底是什么
当你构建 Cocos Creator 生成的 Android 工程时,Gradle 的控制台或日志里可能会输出这样一句:
NDK not configured. Download it with SDK Manager. Preferred NDK version is "23.1.7779620".注意这里的“23.1.7779620”就是 NDK r23c 的完整版本号写法。NDK 有两种版本号表达方式:一种是 r 系列命名(r21e、r23c),另一种是 Android SDK Manager 里显示的长版本号(21.4.7075529、23.1.7779620)。两者是同一件事的两种写法。
这个 Preferred NDK version 是从哪里来的?直接来源是 Cocos 生成的 Android 工程里的native/engine/android/CMakeLists.txt或工程 gradle.properties 里的android.ndkVersion设置。Cocos 构建插件在生成工程时,会把自己已知的默认 NDK 版本号写进去。
所以,最稳的做法不是看安装教程里推荐的 NDK 版本,而是直接看你的项目构建时报错里提示的 Preferred NDK version,或者去项目目录下查native/engine/android里搜索ndkVersion字段。
2.2 主流 Cocos Creator 版本对应 NDK 参考表
下面表格是这几个版本的实际使用经验,参考一下,但最终以你项目里的 ndkVersion 字段为准:
| Cocos Creator 版本 | 推荐 NDK 版本 | 对应 SDK Manager 版本号 | 备注 |
|---|---|---|---|
| 2.0.x - 2.2.x | r17c | 17.2.4988734 | 老构建链路,兼容性最稳 |
| 2.3.x - 2.4.x | r18b / r17c | 18.1.5063045 / 17.2.4988734 | 部分项目用 r21 也能跑,但不建议 |
| 3.0.x - 3.4.x | r21e | 21.4.7075529 | 3.x 早期版本,CMake 3.18.1 搭配 |
| 3.5.x - 3.6.x | r21e / r22 | 21.4.7075529 / 22.1.7171670 | 22 较为稳定 |
| 3.7.x | r23c | 23.1.7779620 | 官方强推 r23c |
| 3.8.x | r23c | 23.1.7779620 | 3.8.0 到 3.8.5 基本都用 r23c |
| 3.8.6+ / 3.9 预览 | r25c 或 r26d | 25.2.9519653 / 26.1.10909125 | 新版本有逐步上探,仍需以工程配置为准 |
看到这个表,你会发现一个规律:Cocos Creator 对 NDK 版本的推荐是滞后的。3.0 都出来了,还推荐 r21e;3.8 时代还在用 r23c。原因是引擎测试团队只对特定 NDK 版本做了完整回归,追求的是确定性,不是新特性。
2.3 选了错误版本的症状对照
NDK 版本装错,报错方式多种多样,很多新手以为是自己代码写错了,其实根子在环境。我给你整理几个高频症状:
编译阶段的 undefined reference:例如报
undefined reference to 'ANativeWindow_setBuffersTransform'。这类问题的典型原因是 NDK 版本太老,sysroot 里没有符号;或者反过来,NDK 版本太新,某些 API 改名或不推荐了,Cocos 引擎的适配代码没跟上。C++ 标准库相关报错:例如
fatal error: 'bits/...' file not found,或者链接时找不到libc++_static.a。这说明 NDK 的 STD 库实现和引擎编译参数不匹配,多数是 NDK 版本跨度过大。找不到编译工具链:比如报
Unable to locate ndk-build或Cannot determine the ABI of the NDK。这种情况一般是 SDK Manager 里虽然勾了 NDK,但装出来的目录结构和 Cocos 预期不一致,例如缺少build/tools子目录。构建成功但安装运行崩溃:这种最坑。一般表现为启动时加载 so 库失败,日志里有
dlopen failed: library "libc++_shared.so" not found。它说明 NDK 编译出来的 so 需要的 C++ 运行库没有被正确打进 APK 里,版本管道出问题。
理解这些症状之后,版本选择就不再是“碰运气”了,而是排查逻辑的第一站。
3. 把 SDK / NDK / JDK / CMake 环境一次配齐
理论基础讲完,下面直接进入实际操作。我的目标不是把所有步骤都列出来平铺直叙,而是把容易出错、容易被教程忽略的关键点拎出来讲透。
3.1 一次装好 Android Studio、SDK 和 Side-by-side NDK
Android Studio 不是 Cocos 打包的绝对必要组件,但它自带的 SDK Manager 太好用了,所以我一直建议先装它。装上之后,打开 SDK Manager,你会看到几个页签:SDK Platforms、SDK Tools。
关键操作在这几个点上:
SDK Platforms 里建议勾一个 Android 10(API 29)或 Android 12(API 31)以上的 Platform,覆盖你 targetSdkVersion 的需求。Cocos 3.8 生成工程默认的 compileSdkVersion 是 31 起,如果你只装了 API 28,构建时会报
failed to find target with hash string 'android-31'。SDK Tools 页签里,务必展开右下角的 “Show Package Details” 选项。这一步被无数教程忽略,但不展开的话你只能装到最新版 NDK,根本选不了 r23c 这种历史版本。
勾选 NDK (Side by side),展开子项,选 23.1.7779620,这就是 NDK r23c。如果提示需要下载,认准这个版本号。
同时勾选 CMake 和 LLDB。CMake 建议选 3.18.1 或 3.22.1,这个版本 Cocos 3.x 验证得较多。别不装 CMake,否则后续会死在 Ninja 报错上。
SDK Tools 里如果看到 Android SDK Platform-Tools 和 Android SDK Build-Tools,也一起勾上。Build-Tools 具体选哪个版本,看构建日志提示。
这些组件下载完成后,路径在 Windows 一般是%LOCALAPPDATA%\Android\Sdk,macOS 是~/Library/Android/sdk,记下这个路径,等会儿要在 Cocos Creator 里填。
3.2 在 Cocos Creator 里配置 SDK、NDK、CMake 路径
打开 Cocos Creator,不同版本菜单入口不同:
- 2.4.x:菜单栏选择
文件→设置→外部程序。 - 3.x:菜单栏选择
编辑器→偏好设置→外部程序或Cocos App -> 偏好设置。
这里主要是填三个字段:
| 字段 | 你要填什么 | 容易犯的错 |
|---|---|---|
| Android SDK 路径 | 上面记下的 SDK 根目录,比如~/Library/Android/sdk | 填成platforms/android-31这种子目录 |
| Android NDK 路径 | NDK 的具体版本目录,比如.../sdk/ndk/23.1.7779620 | 填成.../sdk/ndk根目录,或选成同目录下的.temp文件夹 |
| JDK 路径 | JDK 的安装根目录,比如 Android Studio 自带的 JBR 路径或自己装的 JDK 11/17 | 填到 JDK 的bin子目录里 |
关于 NDK 路径,特别提醒一下:SDK 的ndk目录下会有一组版本号子目录,比如23.1.7779620、25.2.9519653。Cocos 配置里要填到23.1.7779620这一层,让 Cocos 看到这个版本目录下的ndk-build和toolchains。
很多人在这一步填了 ndk 的根目录,Cocos 3.x 构建时自动从根目录扫描版本目录,有时能识别,有时不能,踩坑后干脆直接指向具体版本目录最省事。
3.3 用命令行验证配置是否生效
配置完之后,不要直接盲目去构建,先在命令行里验证一遍每个组件是不是真的可用。以 macOS/Linux 为例,Windows 的 PowerShell 思路相同:
# 检查 SDK echo $ANDROID_HOME # 检查 NDK 目录结构 ls $ANDROID_HOME/ndk/23.1.7779620 # 直接看 ndk-build 是否存在 $ANDROID_HOME/ndk/23.1.7779620/ndk-build --version # 检查 CMake ls $ANDROID_HOME/cmake/3.18.1.5044/bin/cmake验证的关键不是“能跑出一条命令”,而是目录结构是否完整。NDK 的一个典型问题是:SDK Manager 下载中断过,导致目录里只有几个文件,没有完整的toolchains目录。这种“残废 NDK”用命令行一查就能发现,但如果直接进 Cocos 构建,你看到的只是莫名其妙的Build command failed。
另外,验证 C++ 运行库文件也是个好习惯。r23c 这个版本里,你应当能在如下路径看到目标 ABI 的库文件:
ls $ANDROID_HOME/ndk/23.1.7779620/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so如果这个文件在,说明 NDK 的基本结构没问题,Cocos 构建大概率能顺利往下走。
3.4 JDK 与 Gradle 版本匹配验证
JDK 的作用经常被低估。Cocos Creator 3.8 生成的 Gradle 工程,AGP 版本一般是 7.x,它要求 JDK 11 或 JDK 17 才能跑;而 2.4 的老工程可能要求 JDK 8。
如果你本机同时存在多个 JDK,强烈建议不要依赖系统 JAVA_HOME 的全局环境变量,而是在 Cocos Creator 的偏好设置里直接指定 JDK 路径,做到项目级隔离。Android Studio 自带的 JBR 目录(macOS 下通常在/Applications/Android Studio.app/Contents/jbr/Contents/Home)默认是 JDK 17,如果你在用 3.8.x,填它省事又稳定;如果还在用 2.x 老项目,那更建议按照老项目的要求单独装一个 JDK 8 指向它。
验证 JDK 版本:
/Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin/java -version4. 真实打包失败现场:几组高频率报错排查链路
环境配置好之后,打包基本能走通。但真正在实践里,报错永远以你意想不到的方式出现。我把自己踩过、同事踩过、社区里见到过的典型报错串成几条完整的排查链路,希望给到你一个排查的思路模板。
4.1 场景一:构建时反复出现 “NDK not configured. Download it with SDK Manager”
这条报错在 3.x 里非常典型。它真正的意思是:Gradle 被要求使用某个 NDK 版本,但在你的 SDK 目录里找不到对应的版本目录。
排查链路:
第一步,看报错后半部分的版本号。比如:Preferred NDK version is "23.1.7779620",这就是重点。
第二步,去你配置到 Cocos 里的 SDK 根目录下的ndk文件夹看一眼,有没有23.1.7779620这个文件夹。
第三步,如果找不到,两种原因:一是 SDK Manager 里根本没装这个 NDK;二是装了但被装到了别的 SDK 路径下(比如 Android Studio 默认 SDK 路径和你填到 Cocos 里的 SDK 路径不是同一个)。我在公司电脑上遇到过,系统里有两个 SDK,Android Studio 用的 A 路径,Cocos 里填的是 B 路径,NDK 全在 A 路径里,B 路径自然找不到。
解决方案:要么去 SDK Manager 补装对应版本,要么把 Cocos 里的 SDK 路径改成另一个更全的路径。注意,改完路径后 Cocos 的构建缓存可能会残留,建议把build/android目录清掉重新构建。
4.2 场景二:CMake was unable to find a build program corresponding to 'Ninja'
这条报错意味着:Gradle 已经找到了 CMake,但 CMake 在生成构建系统时找不到 Ninja,无法继续。
排查链路:
第一步,检查你有没有通过 SDK Manager 安装 CMake。如果没有,立刻补装。这个报错有一半以上是 CMake 压根没装。
第二步,如果装了 CMake 还是报错,去 SDK 的cmake目录下看,有没有真正包含bin/cmake的版本目录。SDK Manager 有时候会在下载失败后留下一个空壳目录,比如3.22.1.2517里面没有内容,CMake 根本不可用。
第三步,确认工程是不是用的 CMake。旧版本 2.x 项目不走 CMake,这条报错不常见;3.x 项目如果是自定义原生工程配置有问题,也可能触发这个分支。
解决方案:最省事是重新在 SDK Manager 里卸载再安装 CMake,安装后确认目录下有bin目录。装完以后,把 Cocos 生成的本机构建缓存清一次,不要让 Gradle 复用旧的失败缓存。
4.3 场景三:Build command failed,日志里有 undefined reference 或找不到头文件
这是非常宽泛的一类报错,常见于 NDK 版本和引擎版本错配。出现后先别急着琢磨引擎源码,按这个顺序逐步排查:
第一步,确认你用的 NDK 版本是不是 Cocos 的推荐版本。在 Cocos 构建控制台里,展开详情日志,通常能看到当前使用的 NDK 路径。如果日志里写着/ndk/26.2.11394306,而你用的是 Cocos 3.8,明显就是它的问题了。
第二步,检查构建日志里 clang 的版本。NDK r23c 对应 clang 14,r25 对应 clang 14/15,r26 对应 clang 17。clang 版本过高时,Cocos 引擎源码里某些写法会触发新编译器的报错,例如:
error: 'templete' is deprecated之类。
第三步,把 NDK 切回推荐版本,清理构建缓存,重试。这一步能解决八成的“引擎代码报错”。
4.4 场景四:More than one file was found with OS independent path 'lib/arm64-v8a/libc++_shared.so'
这条报错的内存含义是:APK 打包时,有两个不同的库来源都往lib/arm64-v8a/塞同一个文件,Gradle 不知道用哪个,直接罢工。
排查链路:
第一步,平时我们编译 C++ 库时,Cocos 工程里有一个jniLibs或src/main/jniLibs目录,里面可能手动放了 libc++_shared.so;同时 Gradle 构建过程又会从 NDK 目录里自动引入一份。两边重复了。
第二步,打开你的 Android 工程目录,检查src/main/jniLibs/arm64-v8a/下有没有手动拷贝的 .so 文件。如果有,删除。
第三步,如果删了还报,就看 Gradle 依赖里是否有多个库模块都声明了自己的 .so,这类问题多出在用了多个自定义原生插件时。解决方案是在工程的build.gradle里加:
android { packagingOptions { pickFirst 'lib/*/libc++_shared.so' } }注意,这属于治标方案。如果要治本,应该查清楚是哪个依赖重复了。但在 Cocos 默认工程下,手动拷贝的 so 文件才是主要来源,删掉就好。
5. 多版本共存与工程配置落地方案
到了第五节,默认你已经成功打出一个包了。接下来要面对的,是比“第一次跑通”更现实的问题:维护不同时期的多个项目,用什么策略管理 NDK 版本。
5.1 一边做 2.4 老项目,一边接 3.8 新版本,NDK 怎么管理
这是很多商业团队的常态。老项目在维护,新项目在研发,两个项目的 NDK 需求完全不同。
核心策略是:不要试图用全局环境变量解决所有问题,而是让每个项目自己指定 NDK 路径。
在 Cocos Creator 里,全局偏好设置给的是一个默认值,但针对每个项目的构建发布面板,你可以在配置 NDK 路径时手动改成该项目的专用 NDK 目录。所以就算全局默认是 r23c,老项目构建时改成 r18b 也是可以的。
这样的好处是,NDK 版本不再是“全局状态”,而是“项目配置的一部分”。新同事入职,拉代码、装好 Cocos、按文档设置全局 SDK 路径,再按项目 README 设置 NDK 目录,就不会因为环境差异踩坑。
5.2 修改工程级配置而不是污染全局
Cocos Creator 3.x 生成的 Android 工程里,native/engine/android/CMakeLists.txt或者gradle.properties里一般会放 NDK 版本相关信息。例如gradle.properties里可能有:
android.ndkVersion=23.1.7779620如果你要临时切换 NDK 版本,也可以直接改这个文件,把它改成你 SDK 里已经装的版本号。但要注意:Cocos Creator 每次重新生成工程时,可能会根据引擎的默认配置把它覆盖回去。所以我的习惯是,如果只是临时验证,直接改文件;如果要长期生效,尽量在 Cocos Creator 的构建面板里完成配置,让构建插件自己把版本号写进工程。
还有一个容易忽略的点:当你在 SDK Manager 里装了多个 NDK 版本时,Gradle 默认会取它要的那个版本,不受系统 PATH 影响。所以系统 PATH 里的 NDK 其实在 3.x 时代已经不是必需品了。保留 NDK_ROOT 环境变量反而可能干扰部分判断,如果你在 3.x 下出现莫名其妙的 NDK 路径错乱,把环境变量里的 NDK_ROOT、ANDROID_NDK_HOME 清掉重试一下,往往就好了。
5.3 给新人的环境配置检查清单
最后整理一份简单直接的检查清单,每次新项目开始之前对着过一遍,能省掉一半的环境报错时间:
| 检查项 | 标准 |
|---|---|
| Android SDK 路径 | 必须是 SDK 根目录,不是 platforms 子目录 |
| NDK 版本 | 与引擎匹配,3.8 用 r23c / 23.1.7779620 |
| NDK 目录完整性 | 有 toolchains、sysroot、ndk-build 文件 |
| CMake 已安装 | SDK 的 cmake 目录下有 bin/cmake |
| JDK 版本 | 3.8 用 17 或 11,2.x 用 8 |
| 构建缓存 | 切换 NDK 后清build/android目录 |
| 环境变量 | 3.x 下保持干净的 ANDROID_HOME,不设 NDK_ROOT 反而更稳 |
6. 最后再分享两个我个人的土办法
说这些可能有点私货,但确实是实际干活时才用到的经验。
第一个是“看日志要看到关键行”。Cocos 构建失败时,构建面板只给一个笼统的Build command failed,真正的错误藏在日志中间。遇到构建失败,先别在面板上盯着那一行红字看,直接去 Cocos 工程目录下的build/android里找日志文件,比如log.txt或者 Gradle 输出的控制台日志,然后搜error、fatal、undefined reference这种关键词。定位到第一个完整错误,再去诊断环境问题。很多新手死磕面板那行红色提示,浪费半天,而实际上那行字根本不是根因。
第二个是“切换 NDK 版本后,一定要清缓存”。Gradle 的缓存机制、CMake 的缓存机制、Cocos 的构建缓存机制,三层缓存叠加,任何一层残留着旧版本的信息,都会做出让你看不懂的选择。我见过最典型的情况:SDK Manager 里装了 r23c 和 r25 两个版本,Cocos 配置指向 r23c,构建却一直在用 r25 编译,就是因为某层缓存里记录了旧的 NDK 路径。清理方案是删掉build/android目录后重新构建,必要时顺手清一下工程的.gradle缓存目录。这个动作虽然简单,但能排除掉一半的“玄学报错”。
整套环境配下来,你会发现 NDK 版本选择其实不是技术难题,而是信息匹配问题。搞清楚 Cocos 构建脚本期望的版本号,装上对应 NDK,再保持本机环境干净,Android 打包这条链路就可以稳定跑很久了。