1. 项目概述:为什么Unity2020打包Android必须亲手配齐JDK、SDK、NDK三件套?
Unity2020发布Android包这件事,表面看只是点一下“Build & Run”,但背后卡住90%新手的,从来不是C#脚本写得对不对,而是构建环境里那三座沉默的大山——JDK、SDK、NDK。我带过二十多个Unity小团队,几乎每支队伍都经历过凌晨两点还在查“Failed to run ‘java -version’”“NDK not configured”“SDK path is invalid”这类报错。它们不是报错,是系统在喊话:“你连地基都没打平,别急着盖楼。”
这三者不是可选插件,而是Unity Android构建链路上不可绕行的硬性依赖节点:JDK负责把C#编译后的IL代码和Java层桥接逻辑翻译成JVM能执行的字节码;SDK提供Android系统API的本地映射、ADB调试通道、模拟器运行时以及AAPT资源打包工具;NDK则专攻C/C++原生模块——比如你要接入高精度IMU传感器、调用OpenCV图像处理库、或者用FFmpeg做硬解码,没有NDK,这些功能在Android上根本跑不起来。Unity2020默认不捆绑任何一套,它只认你本地配置好的路径,且对版本有明确要求:JDK必须是8或11(官方明确不支持17+),SDK API Level至少29(对应Android 10),NDK必须是r21e或r23b(r22已被弃用)。这不是Unity故意设门槛,而是Android生态本身演进的结果——Google从Android 10开始强制要求64位应用,NDK r21e是首个完整支持ARM64-v8a ABI的稳定版;而JDK 17引入的强封装机制会直接导致Unity的Android Gradle插件加载失败。
所以这篇教程不讲“怎么点按钮”,只讲“为什么必须这样装、为什么必须装这个版本、装错一个字符会触发哪条错误链”。我会带你从零开始,在Windows或macOS上亲手搭出一条稳如磐石的Android构建流水线。适合三类人:刚从Unity2019升级过来发现打包报错的老手、第一次接触移动开发的Unity新人、以及被外包团队甩锅“环境问题”后自己想亲手验证的技术负责人。接下来所有操作,我都已在两台物理机(Win11 + M1 Mac)上实测通过,每一步截图、日志、路径都经得起回溯。
2. 环境设计逻辑与版本选型依据:拒绝盲目下载,理解每个组件的职责边界
2.1 JDK:不是越新越好,而是要和Unity的Gradle插件握手成功
Unity2020使用的Android构建系统基于Gradle 6.1.1,而该版本Gradle的Java兼容性有明确限制:仅支持JDK 8u202+ 或 JDK 11.0.2+。JDK 17虽然已是LTS版本,但Gradle 6.1.1的底层ClassLoader机制与JDK 17的强模块化(Strong Encapsulation)存在冲突,会导致Unity在生成build.gradle时抛出java.lang.module.FindException: Module java.base not found。这不是Unity的bug,是Gradle 6.x系列的设计约束。
我试过三种方案:
- JDK 17 + 强制修改gradle.properties:添加
org.gradle.jvmargs=--add-opens java.base/java.lang=ALL-UNNAMED,但Unity每次Build都会重写该文件,治标不治本; - JDK 11 + 手动降级Gradle版本:需修改Unity安装目录下的
Editor/Data/PlaybackEngines/AndroidPlayer/Tools/gradle/lib/gradle-core-6.1.1.jar,风险极高,且可能破坏Unity内部签名验证; - JDK 11.0.2 + Unity官方推荐组合:零修改、零冲突、长期稳定。
因此,我们锁定JDK 11.0.2。注意不是“JDK 11”,而是精确到小版本号。Oracle官网已下架旧版JDK 11.0.2,但Adoptium(现为Eclipse Temurin)仍提供安全更新的镜像。下载地址必须是:https://adoptium.net/temurin/releases/?version=11 —— 进入后选择11.0.2+9(Build 9是该版本的最终安全补丁号),操作系统选对应平台,Package Type选JDK(非JRE)。
提示:不要用“jdk-11_windows-x64_bin.exe”这类命名模糊的安装包。Temurin的包名格式为
OpenJDK11U-jdk_x64_windows_hotspot_11.0.2_9.zip,解压即用,无需安装程序,避免注册表污染。
2.2 SDK:不是全量安装,而是按Unity需求精简裁剪
Android SDK包含数百个组件,但Unity真正需要的只有5个核心模块:
- Android SDK Platform-Tools:含
adb命令,用于设备连接、日志抓取、APK安装; - Android SDK Tools(Legacy):含
android命令行工具(虽已废弃,但Unity2020的旧版Gradle仍调用它生成keystore); - Android SDK Build-Tools 29.0.3:Unity2020默认调用的AAPT2版本,用于资源编译;
- Android SDK Platform 29:对应Android 10 API,是Unity2020的最低要求;
- Android Emulator:非必需,但调试时比真机更可控。
其他如“Android SDK Sources for Android 29”“Google APIs”“Google Play services”等,Unity打包时完全不读取。全量安装不仅浪费20GB磁盘空间,还会因组件版本冲突导致SDK location not found错误。
关键细节:SDK Manager的GUI界面在Unity2020中已被弃用,我们必须用命令行工具sdkmanager精准控制。它位于SDK根目录的cmdline-tools/latest/bin/下,但该路径默认不存在——你需要手动创建cmdline-tools/latest/文件夹,并将sdkmanager二进制文件放进去。这是Google在2020年强制推行的SDK目录结构变更,Unity2020尚未适配,必须人工补位。
2.3 NDK:不是最新版最稳,而是r21e与Unity2020的ABI契约
NDK版本选择的核心逻辑是ABI(Application Binary Interface)兼容性。Unity2020默认生成的.so库目标架构是armeabi-v7a和arm64-v8a,而NDK r21e是首个同时提供完整arm64-v8a工具链(aarch64-linux-android-clang++)且修复了__atomic_fetch_add_8符号缺失问题的版本。r22在Android 11上会出现undefined reference to __atomic_load_8链接错误;r23b虽支持,但Unity2020的Native Plugin加载器未适配其新的CMake工具链路径。
因此,我们必须用NDK r21e。官方下载地址:https://developer.android.com/ndk/downloads/older_releases#ndk-r21e-downloads —— 注意选择NDK r21e (August 2020),不是“Latest Stable”。下载后解压得到android-ndk-r21e文件夹,路径中不能包含空格或中文(如C:\Program Files\或/Users/张三/),否则Unity会解析失败。这是Unity2020的硬伤,无法通过配置修复。
3. 全流程实操:从零开始搭建可验证的Android构建环境
3.1 JDK安装与环境变量配置(Windows/macOS双路径)
Windows平台实操步骤:
- 访问https://adoptium.net/temurin/releases/?version=11,下载
OpenJDK11U-jdk_x64_windows_hotspot_11.0.2_9.zip; - 解压到固定路径,例如
D:\JDK\jdk-11.0.2+9(路径不含空格、无中文、非系统盘); - 右键“此电脑”→“属性”→“高级系统设置”→“环境变量”;
- 在“系统变量”中新建变量:
- 变量名:
JAVA_HOME - 变量值:
D:\JDK\jdk-11.0.2+9(注意:不带bin目录);
- 变量名:
- 编辑“系统变量”中的
Path,新增一行:%JAVA_HOME%\bin; - 打开新CMD窗口,执行
java -version,输出应为:
若提示“不是内部命令”,检查openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.2+9) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.2+9, mixed mode)Path是否漏掉%JAVA_HOME%\bin,或JAVA_HOME路径末尾是否多了\。
macOS平台实操步骤:
- 下载
OpenJDK11U-jdk_aarch64_mac_hotspot_11.0.2_9.tar.gz(M1芯片)或x64_mac(Intel); - 解压到
/Library/Java/JavaVirtualMachines/,得到/Library/Java/JavaVirtualMachines/jdk-11.0.2+9.jdk; - 编辑
~/.zshrc(M1)或~/.bash_profile(Intel),添加:export JAVA_HOME=$(/usr/libexec/java_home -v 11.0.2) export PATH=$JAVA_HOME/bin:$PATH - 执行
source ~/.zshrc,再运行java -version验证。
注意:Windows用户若使用PowerShell,需在PowerShell中同样配置
$env:JAVA_HOME和$env:Path,因为Unity Editor启动时读取的是PowerShell环境变量,而非CMD。
3.2 SDK下载、精简安装与路径初始化
第一步:获取命令行SDK Manager
- 访问https://developer.android.com/studio#command-tools,下载“Command line tools only”;
- 解压得到
cmdline-tools文件夹; - 进入SDK根目录(如
D:\Android\Sdk),创建子目录cmdline-tools\latest\; - 将下载的
cmdline-tools\bin\下所有文件(sdkmanager.bat、avdmanager.bat等)复制到D:\Android\Sdk\cmdline-tools\latest\。
第二步:用sdkmanager精准安装5个必需组件
打开CMD,执行:
cd /d D:\Android\Sdk\cmdline-tools\latest sdkmanager --sdk_root="D:\Android\Sdk" "platform-tools" "platforms;android-29" "build-tools;29.0.3" "tools" "emulator"注意:--sdk_root参数必须显式指定,否则sdkmanager会默认使用C:\Users\用户名\AppData\Local\Android\Sdk,与Unity配置路径不一致。
第三步:验证SDK完整性
执行sdkmanager --list_installed,输出中必须包含:
platform-tools | 34.0.5 platforms;android-29 | 3 build-tools;29.0.3 | 29.0.3 tools | 26.1.1 emulator | 32.1.12若缺少任一项,重新运行安装命令。特别注意build-tools;29.0.3——Unity2020的aapt2调用硬编码了该版本号,安装29.0.2或29.0.4都会报错AAPT2 aapt2-29.0.3-5434570-windows Daemon #0 failed to shut down within 10 seconds。
3.3 NDK r21e部署与Unity路径绑定
NDK部署要点:
- 下载
android-ndk-r21e-windows-x86_64.zip(Windows)或-darwin-x86_64.zip(macOS); - 解压到
D:\Android\ndk\android-ndk-r21e(Windows)或/Users/xxx/Library/Android/sdk/ndk/android-ndk-r21e(macOS); - 确保路径中无空格、无中文、无特殊符号(如
&、#); - 不要将NDK放在SDK目录内!Unity2020要求NDK路径独立于SDK,否则会触发
NDK path contains spaces or invalid characters校验失败。
Unity中绑定路径:
- 打开Unity Hub → 选择Unity2020.x版本 → 点击右上角“Settings” → “External Tools”;
- 勾选“Android” → 在“JDK”栏填入
D:\JDK\jdk-11.0.2+9; - 在“SDK”栏填入
D:\Android\Sdk; - 在“NDK”栏填入
D:\Android\ndk\android-ndk-r21e; - 点击“Apply”,Unity会自动检测并显示版本号(如JDK 11.0.2、SDK 29、NDK r21e)。
实操心得:若Unity显示“NDK not configured”,先检查路径末尾是否有多余
\(如D:\Android\ndk\android-ndk-r21e\),Unity会将其识别为无效路径;其次检查NDK文件夹内是否存在source.properties文件——这是NDK的版本标识文件,缺失则Unity无法识别。
3.4 Unity项目级配置:Player Settings深度调优
完成全局环境配置后,必须在项目中做三处关键设置:
- Target Architectures:在
File → Build Settings → Player Settings → Publishing Settings中,勾选ARM64(必选)和ARMv7(可选)。若只勾选ARMv7,生成的APK将无法在华为Mate 40、小米12等新机型安装; - Minify:
Publishing Settings → Minify → Release选择None。Unity2020的ProGuard混淆器与JDK 11存在反射调用冲突,开启后会导致ClassNotFoundException; - Custom Main Manifest:若项目需自定义权限(如
<uses-permission android:name="android.permission.CAMERA"/>),必须勾选Custom Main Manifest,然后在Assets/Plugins/Android/AndroidManifest.xml中编辑——Unity会合并该文件与自动生成的Manifest,而非覆盖。
最后验证:
创建一个空场景,添加一个TextMeshProUGUI文本,脚本中写Debug.Log("Android Build Test OK");;File → Build Settings → Platform选Android → Build;
若生成test.apk且无红色报错,即环境配置成功。将APK拖入安卓手机安装,打开后Logcat应输出该日志。
4. 常见问题与排查技巧实录:那些官方文档不会写的坑
4.1 经典报错链路与根因定位表
| 报错信息 | 根本原因 | 排查步骤 | 修复方案 |
|---|---|---|---|
Failed to run 'java -version' | JAVA_HOME路径错误或Path未包含%JAVA_HOME%\bin | 在CMD中执行echo %JAVA_HOME%和where java | 检查JAVA_HOME是否指向JDK根目录(非bin),where java是否返回%JAVA_HOME%\bin\java.exe |
SDK root directory does not exist | Unity中SDK路径填写了D:\Android\Sdk\platforms等子目录 | 在Unity External Tools中查看SDK路径是否以Sdk结尾 | 删除路径末尾的\platforms等,确保是SDK根目录 |
NDK not configured. Download it with SDK manager. | NDK路径含空格/中文,或source.properties文件缺失 | 进入NDK文件夹,用记事本打开source.properties | 重命名NDK文件夹为纯英文,或重新下载完整r21e包 |
AAPT2 error: check logs for details | build-tools;29.0.3未安装或版本号不匹配 | 运行sdkmanager --list_installed | findstr "build-tools" | 重新执行sdkmanager "build-tools;29.0.3",确认安装成功 |
Gradle build failed: Could not resolve all artifacts | Unity使用了旧版Gradle插件,与JDK 11.0.2的TLS协议不兼容 | 查看Temp/gradleOut/build.gradle第3行distributionUrl | 手动修改为distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip |
4.2 隐藏陷阱:Windows Defender与macOS Gatekeeper的干扰
Windows场景:
Windows Defender的“受控文件夹访问”功能会拦截sdkmanager对build-tools目录的写入,导致安装后D:\Android\Sdk\build-tools\29.0.3\为空。现象是sdkmanager --list_installed显示已安装,但D:\Android\Sdk\build-tools\下无29.0.3文件夹。
解决方案:
- 打开“Windows安全中心”→“病毒和威胁防护”→“勒索软件防护”→“受控文件夹访问”→“关闭”;
- 重新运行
sdkmanager安装命令; - 安装完成后,可重新开启该功能。
macOS场景:
macOS Catalina+系统会对sdkmanager执行文件标记“已损坏”,双击运行提示“已损坏,无法打开”。这是因为sdkmanager是Java写的脚本,未通过Apple Developer ID签名。
解决方案:
在终端执行:
xattr -d com.apple.quarantine /path/to/sdkmanager将/path/to/sdkmanager替换为你实际的路径,例如/Users/xxx/Library/Android/sdk/cmdline-tools/latest/sdkmanager。
4.3 真机调试必知的ADB授权链
即使环境配置成功,首次连接安卓手机仍可能卡在“Waiting for device”。这不是Unity问题,而是ADB授权未通过:
- 手机开启“开发者选项”(连续点击“关于手机”中“版本号”7次);
- 开启“USB调试”;
- 用USB线连接电脑,手机弹出“允许USB调试吗?”对话框,勾选“始终允许”,点击“确定”;
- CMD中执行
adb devices,应显示设备序列号+device状态。
若显示unauthorized,说明手机未授权。此时拔掉USB线,重启手机ADB服务:
adb kill-server adb start-server再重新连接。这是安卓系统级安全机制,与Unity环境无关,但90%的新手会误以为是Unity配置失败。
4.4 Unity2020特有的Gradle缓存污染问题
Unity2020在首次Build时会生成Temp/gradleOut/目录,其中包含gradle/wrapper/gradle-wrapper.properties。若你中途更换过JDK或SDK路径,该文件中的distributionUrl可能仍指向旧版本Gradle,导致后续Build持续失败。
清理方法:
- 关闭Unity Editor;
- 删除项目根目录下的
Temp/文件夹(Unity会自动重建); - 删除
Library/文件夹(耗时较长,但可彻底清除缓存); - 重新打开Unity,再次Build。
实操心得:我曾遇到一个案例,客户提供的项目
Temp/gradleOut/gradle/wrapper/gradle-wrapper.properties中distributionUrl指向gradle-5.6.4-bin.zip,而Unity2020要求6.1.1。手动修改后Build成功,但下次打开Unity又恢复为5.6.4——根源是ProjectSettings/EditorBuildSettings.asset中缓存了旧Gradle路径。最终解决方案是删除整个Library/,让Unity重新生成全部元数据。
5. 进阶技巧与长期维护建议:让环境持续稳定运行的实战经验
5.1 创建可复用的环境检查脚本(Windows Batch / macOS Shell)
手动验证每个组件太耗时,我编写了一个5分钟就能跑完的自检脚本,放在项目根目录下,每次换新机器或重装系统时双击运行:
Windows版check_android_env.bat:
@echo off echo === JDK Check === if not defined JAVA_HOME ( echo ERROR: JAVA_HOME not set exit /b 1 ) %JAVA_HOME%\bin\java -version 2>nul || ( echo ERROR: java -version failed exit /b 1 ) echo JDK OK echo === SDK Check === if not exist "D:\Android\Sdk\platforms\android-29" ( echo ERROR: SDK Platform 29 not found exit /b 1 ) if not exist "D:\Android\Sdk\build-tools\29.0.3\aapt2.exe" ( echo ERROR: Build-tools 29.0.3 not found exit /b 1 ) echo SDK OK echo === NDK Check === if not exist "D:\Android\ndk\android-ndk-r21e\source.properties" ( echo ERROR: NDK r21e source.properties missing exit /b 1 ) echo NDK OK echo === All Checks Passed! === pausemacOS版check_android_env.sh:
#!/bin/bash echo "=== JDK Check ===" if [ -z "$JAVA_HOME" ]; then echo "ERROR: JAVA_HOME not set" exit 1 fi "$JAVA_HOME/bin/java" -version >/dev/null 2>&1 || { echo "ERROR: java -version failed" exit 1 } echo "JDK OK" echo "=== SDK Check ===" if [ ! -d "$HOME/Library/Android/sdk/platforms/android-29" ]; then echo "ERROR: SDK Platform 29 not found" exit 1 fi if [ ! -f "$HOME/Library/Android/sdk/build-tools/29.0.3/aapt2" ]; then echo "ERROR: Build-tools 29.0.3 not found" exit 1 fi echo "SDK OK" echo "=== NDK Check ===" if [ ! -f "$HOME/Library/Android/sdk/ndk/android-ndk-r21e/source.properties" ]; then echo "ERROR: NDK r21e source.properties missing" exit 1 fi echo "NDK OK" echo "=== All Checks Passed! ==="5.2 多Unity版本共存时的路径隔离策略
团队中常有Unity2019、2020、2021并存的情况。若所有版本共用同一套JDK/SDK/NDK,一旦某版本升级导致路径变更,其他版本立即失效。我的做法是:
- JDK:为每个Unity版本分配独立JDK,如
D:\JDK\unity2020\jdk-11.0.2+9、D:\JDK\unity2021\jdk-17.0.1+12; - SDK:共用一套SDK(因API Level向下兼容),但为每个Unity版本创建软链接:
mklink /D "D:\Unity2020\Sdk" "D:\Android\Sdk" - NDK:严格隔离,Unity2020用r21e,Unity2021用r23b,路径完全独立。
这样既节省磁盘空间,又避免版本冲突。Unity Hub的External Tools设置支持为每个Unity版本单独配置路径,无需全局修改。
5.3 当Unity突然报“Android SDK is not installed”时的终极诊断法
这个报错往往出现在环境明明配置正确的情况下。我的诊断流程是:
- 查Unity日志:打开
C:\Users\[用户名]\AppData\Local\Unity\Editor\Editor.log,搜索AndroidSdkRoot,确认Unity读取的实际路径; - 查Registry(Windows):运行
regedit,定位HKEY_CURRENT_USER\Software\Unity Technologies\Unity Editor 5.x\AndroidSdkRoot,对比是否与Unity界面中设置的路径一致; - 查Unity Hub缓存:删除
C:\Users\[用户名]\AppData\Roaming\UnityHub\settings.json,重启Unity Hub,重新配置路径。
90%的“SDK未安装”报错,根源是Unity Hub的settings.json缓存了旧路径,而Unity Editor读取的是该文件而非界面输入值。删除后重新配置,一劳永逸。
我在实际项目中踩过的最大坑,是某次Windows系统更新后,adb命令被重置为系统自带的旧版本(1.0.32),导致adb devices返回空列表。花了3小时排查,最后发现是D:\Android\Sdk\platform-tools\adb.exe被Windows更新覆盖。解决方案是:将platform-tools文件夹重命名为platform-tools-bak,再用sdkmanager重新安装一次。这个细节,官方文档永远不会提,但却是真实世界里高频发生的故障。