尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity安卓打包Gradle build failed全攻略:从日志定位到环境排查

Unity安卓打包Gradle build failed全攻略:从日志定位到环境排查 “Gradle build failed”这行红字我猜在座的Unity开发者都不陌生。尤其是打包安卓的时候你可能刚把Player Settings里的包名填好、IL2CPP选上、ARM64勾上结果Build按钮点下去等了五分钟Console里刷出一整页Building Library 和异常堆栈最后就浓缩成这一句冰冷的“Gradle build failed”。这个报错可以说是Unity安卓打包里最著名的“拦路虎”了因为它不是某个单一原因导致的而是整个Gradle构建过程中的任何一个环节出错最终都会统一收口到这句话上。不过别慌这句话背后的问题其实就那么几大类环境配置、网络依赖、SDK/NDK完整度、代码与资源冲突。大多数情况下你缺的不是“运气”而是一套系统的排查思路。这篇文章我就结合自己这几年在Unity安卓打包上踩过的坑、翻过的日志从头到尾把Gradle build failed的常见原因和对应解法拆开来讲目标是让你以后再看到这个红字能直接按照清单一步步定位而不是凭感觉瞎试。1. 报错机制与日志定位别被“Gradle build failed”带偏了1.1 Unity打包安卓时Gradle到底在做什么Unity从2019版本开始安卓打包底层就已经完全切换到Gradle体系了。你在Build Settings里点Build之后Unity会先做资源导出、C#脚本编译、如果有IL2CPP还会做C工程生成然后Unity把这些中间产物组装成一个完整的Gradle安卓工程再调用Gradle去执行真正的构建任务合并Manifest、处理资源、把Java/Kotlin插件代码编译成DEX、打包成APK/AAB、最后签名。整个过程非常长任何一个子任务抛异常最终日志都会以“Gradle build failed”收尾。理解这一点很重要因为很多人一看到Gradle build failed第一反应是去改Gradle配置其实问题可能根本不在Gradle本身。可能是你的Android SDK缺了一个平台组件可能是某个插件依赖下载超时也可能是工程里的某个资源文件命名不规范。所以我的建议是先别急着改配置第一步是定位真正的报错点。1.2 拿到完整日志的四个渠道Unity的Console窗口默认只显示缩短后的错误摘要有时候错误会被截断真正有用的异常信息根本看不到。我常用的查日志方式有四种Console窗口里展开那条错误。在Console里选中报错后下面的堆栈区有时会多显示几行但很多时候还是不够完整。查看Build Report。在Build Settings窗口底部勾选Build Report后构建结束后会生成一份报告里面有构建步骤的耗时和部分错误信息。直接打开工程目录下的Temp/StagingArea/build/gradle.log。这是Gradle在构建时实时输出的日志信息量最全很多Console里看不到的细节都在这里。如果你开启了Custom Gradle Template还可以在Assets/Plugins/Android/目录下找到gradle工程文件用Android Studio打开后在IDE里跑一次gradlew assembleRelease能看到比Unity更直观的报错输出。我个人的习惯是先看第3种Temp/StagingArea/build/gradle.log。从文件开头一直往下翻找第一个出现ERROR或FAILURE的地方因为后面的报错往往是由前面第一个错误引发的连锁反应。这个习惯帮我省了大量瞎折腾的时间。提示不要只看日志最后几行。“BUILD FAILED”是结果不是原因往上翻几页找到第一条异常堆栈才算是真正定位到问题。2. 打包环境三块基石JDK、SDK与NDK的版本匹配2.1 JDK版本不匹配的典型症状Gradle构建是在JVM上运行的Unity自带的OpenJDK和Unity版本之间是配套关系。比如Unity 2019系列一般对应JDK 8Unity 2021系列对应JDK 11Unity 2022/2023系列则开始推荐JDK 17。如果你在Preferences里手动指定了JDK路径或者机器上装过多个版本的Java很容易出现版本不匹配。典型报错长这样Unsupported class file major version 61The supplied javaHome seems to be invalid.Java home is different from the expected location这种问题的解决办法很直接如果不需要自己维护独立的JDK就用Unity自带的那个。在Unity菜单栏选择“Edit → Preferences → External Tools”勾选JDK那栏的“OpenJDK”而不是自己手动填路径。如果你确实需要自定义JDK务必查清楚当前Unity版本对应的JDK主版本用完整的JDK而不是JRE。注意某些精简版或绿化版的JDK缺少编译工具会导致Gradle中途崩溃。遇到类似情况直接换官方的OpenJDK压缩包重新配置别折腾精简版。2.2 Android SDK组件缺失与自动检测SDK是另一个高频问题区。Unity打包安卓并不是装个完整的Android Studio就行而是要保证SDK里包含Unity需要的那几个组件platform-tools、build-tools、指定的platforms/android-XX以及可能用到的NDK和CMake。如果缺了组件Gradle构建时通常会报Failed to find target with hash string android-30SDK component build-tools;30.0.3 is missingUnable to locate adb这种情况我推荐的做法是直接在Unity Hub里补装Android Build Support模块下的SDK和NDK。很多老手喜欢用Android Studio的SDK Manager那个也能装但要注意版本别太新Unity内置的Gradle插件有时候跟不上最新的SDK版本。比如Unity 2021的Gradle插件版本默认是4.x搭配targetSdk 32及以上时需要额外处理否则会报错。如果你在Preferences里发现Unity自动检测到的SDK路径是空的或者你手动指定了一个不完整的SDK目录最简单的办法是让Unity自己重新下载一个。虽然下载时间会长一点但至少保证组件齐全。2.3 NDK与IL2CPP构建的暗坑很多人打包选的是Mono后端所以NDK问题相对隐蔽。但如果你切到IL2CPP ARM64发布到国内主流应用市场基本是标配NDK就是硬需求。NDK版本和Unity版本不匹配时构建会卡在C编译阶段或者报出各种奇怪的编译错误像NDK not configured、Unable to determine NDK version。以Unity 2021.3 LTS为例它推荐使用的是r21e或r23b具体版本在Unity Hub里选Android Build Support时可以看到默认的NDK版本号。我的建议是千万别自己下载最新版NDK来替换Gradle插件和NDK之间不是越新越好而是匹配才稳。如果你项目里有第三方C库NDK升级后还可能出现ABI兼容问题更麻烦。2.4 快速检查环境配置的实操清单按照下面这个清单逐项核对能避免大部分环境类报错Preferences → External Tools → JDK要么用OpenJDK要么确认JDK版本与Unity匹配。Preferences → External Tools → Android SDK确认路径有效且SDK目录下存在platform-tools和platforms。Preferences → External Tools → Android NDK如果使用IL2CPP确认NDK版本匹配且路径有效。Build Settings → Player Settings → Other Settings → Scripting Backend确认选择IL2CPP或Mono时配套的Target Architectures勾选正确。环境配置这块80%的报错都能靠核查这个清单解决掉。剩下的20%就多半是网络和依赖问题了下面重点聊。3. 网络与依赖Gradle构建失败的重灾区3.1 依赖下载失败的真实原因Gradle工程里的repositories默认配置的是Google的Maven仓库和Maven Central。在国内网络环境下访问这两个仓库时经常超时或断连。一旦Gradle拉不到依赖构建就会报Could not resolve com.android.tools.build:gradle:4.x.xCould not GET https://dl.google.com/...Read timed out这个问题的本质是网络连通性不是Gradle配置错误。我第一次遇到时还去挨个检查gradle版本、AGP版本折腾了一晚上最后才发现是仓库访问超时。后来我直接把项目里的仓库镜像换成国内可稳定访问的地址这类问题基本绝迹。3.2 用阿里云镜像替代默认仓库Aliyun的Maven代理仓库是开发者圈子里比较常用的方案。虽然官方文档很少提但它的稳定性和速度在长期实践中是经得考验的。如果你用的是Unity默认的Gradle构建流程操作方式是在Build Settings → Player Settings → Publishing Settings里勾选Custom Gradle Template。Unity会在Assets/Plugins/Android/下生成一个gradleTemplate.properties文件。打开Assets/Plugins/Android/gradleTemplate.properties在文件末尾追加以下内容android.useAndroidXtrue android.enableJetifiertrue org.gradle.jvmargs-Xmx4096m org.gradle.daemontrue如果你还需要修改Gradle的repositories仓库在Assets/Plugins/Android/下创建mainTemplate.gradle如果你已经勾选了Custom Gradle Template这个文件应该已经存在然后找到allprojects或buildscript里的repositories块把阿里云镜像地址加在最前面buildscript { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } google() mavenCentral() } } allprojects { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } google() mavenCentral() } }这里有个细节google()和mavenCentral()要保留不能只留镜像地址。因为镜像代理的毕竟是缓存某些冷门依赖可能没有及时同步保留官方仓库作为兜底避免到时候又冒出来一个“Could not find xxx”的报错。注意如果Unity的Console里报错并提示“Unable to load mainTemplate.gradle”或者“gradleTemplate.properties”相关错误说明模板文件格式有问题。最常见的原因是文件保存成了UTF-8 BOM格式或者行尾用了Windows CRLF但被错误的文本编辑器转换过。建议用VS Code或Notepad重新保存为UTF-8 without BOM。3.3 Gradle发行版本体下载失败的另一个解法除了依赖仓库Gradle本身还有一个wrapper分发包需要下载。Unity在构建时会把对应版本的gradle-x.y-z-all.zip下载到本地。这个下载走的也是国外服务器慢的时候能把人急死。如果你发现报错信息里有Could not download gradle-6.1.1-all.zip或Gradle distribution https://services.gradle.org/distributions/gradle-6.1.1-all.zip的相关字样说明是Gradle发行版下载失败了。解法有两个方案一手动下载并放入wrapper目录。先根据日志找到Gradle版本号手动用浏览器或者迅雷下载对应的zip包放到本机gradle缓存的wrapper目录下。Windows一般是C:\Users\你的用户名\.gradle\wrapper\dists\gradle-6.1.1-all\某串hash\直接把zip放进去并解压让Gradle跳过网络下载。方案二用第三方镜像下载Gradle发行版。国内有一些镜像站提供gradle发行版的加速比如腾讯云镜像。把gradle-wrapper.properties里的distributionUrl改成镜像地址再让Unity重新生成工程。这种方式对Unity自动生成的临时工程来说配置起来麻烦因为每次构建会重新生成但如果你用了Custom Gradle Template这个问题就会好处理很多。3.4 离线构建环境下能不能直接过有时候开发机和企业内网环境完全无法访问外网依赖下载就成了致命问题。我的实测经验是Gradle离线构建在Unity里不是最优方案但可以通过“提前缓存依赖”的方式实现。方法是在有网的机器上用同一Unity版本、同样的模板配置先成功打包一次。这样本地的~/.gradle/caches/modules-2目录下就会缓存所有构建依赖。之后把整个~/.gradle目录拷贝到离线机器对应位置再构建时就不会重新下载。不过要小心如果你换了Unity版本、改了AGP版本缓存的依赖可能不匹配又得重新缓存一份。这是没办法的事情Gradle构建本身就不适合完全离线但提前缓存是实际情况中相对可行的路径。4. 从日志到修复完整排查流程实战4.1 阶段一清理缓存与重试当你第一次看到Gradle build failed时不要急着深挖先做三件事清理临时文件、删除Temp目录、重启Unity。这么做不是碰运气而是因为Unity的增量构建偶尔会留下脏缓存尤其是你中途修改过Manifest或者插件包时旧缓存可能在资源合并阶段产生残留冲突导致明明代码没有问题却总是构建失败。具体操作关闭Unity。删除项目根目录下的Temp文件夹。进入Library目录删除Bee文件夹这个目录里存放的是增量构建中间文件删掉后Unity会重新生成。重新打开Unity再次打包。这个操作我大概用掉了上百次。它能解决不少“看起来莫名其妙”的报错特别是当报错位置在StagingArea下的资源合并阶段时。不过要注意删除Temp和Bee会让下一个构建的增量时间变长因为要重新做一部分中间产物的编译这点等待是值得的。4.2 阶段二定位SDK/NDK组件的缺失项清理缓存后如果还报错就需要动手看日志了。在Temp/StagingArea/build/gradle.log里搜FAILURE或ERROR重点看第一条异常。如果异常信息里出现了SDK、platforms、build-tools、NDK等关键字基本就是环境组件缺失。处理方式比较直接打开Unity Hub找到当前Unity版本的“调制”按钮把Android Build Support下的SDK、NDK、OpenJDK都确认安装一遍。如果你已经装了Android Studio也可以用SDK Manager把缺失的组件补上。但还是那句话版本别追新Unity适配好的版本才是稳定选择。4.3 阶段三依赖冲突与AndroidX迁移问题依赖冲突是另一个重灾区特别是当项目里同时引用了老旧的support库和新的androidx库时Gradle会在依赖解析阶段直接报Duplicate class。典型报错Duplicate class androidx.core.app.CoreComponentFactory found in modules classes.jarDependency androidx.appcompat:appcompat requires libraries and applications that depend on it to compile against version 31 of the Android APIs如果你用的是Unity官方推荐的Android插件框架遇到这种问题通常是因为某个旧的第三方插件还在用com.android.support包。解决办法是在gradleTemplate.properties里确认已经开启android.useAndroidXtrue和android.enableJetifiertrue。把第三方插件升级到支持AndroidX的版本或者找替代插件。如果某个库死活不支持AndroidX那就只能在dependencies里用exclude把它排除掉再手写对应的兼容实现。这不是常规路径不推荐基本功不扎实的同学硬碰优先考虑换库。还有一个非常常见的坑Unity 2019及以上版本在默认模板里已经启用了AndroidX但有些老项目的代码还在用Android.Support.V4.App这类命名空间如果你是自己写的Java代码也要一并迁移到androidx命名空间。4.4 阶段四资源与代码层面的报错如果日志里明确指向某个Gradle任务失败比如mergeReleaseResources、processReleaseManifest、compileReleaseJavaWithJavac那问题就出现在资源或代码层面。mergeReleaseResources失败常见原因是资源文件冲突。比如Android工程里存在同名资源drawable或layout或者资源文件名用了中文、大写字母、特殊字符。解决办法是检查Assets/Plugins/Android/res目录下的文件名全部改成小写字母、数字、下划线的组合。另外如果你用了多个aar/aar插件包每个包里包含相同的资源文件也会触发冲突这时候需要找到引起冲突的aar并排除对应资源。processReleaseManifest失败多半是AndroidManifest.xml里写了不合法内容比如重复声明权限、错误引用activity、使用了系统不允许的权限或者在Manifest中直接写了带数字的key。日志中一般会具体指出哪一行有错你在Assets/Plugins/Android/AndroidManifest.xml里找到对应的位置改掉即可。compileReleaseJavaWithJavac失败对应的就是你的Java/Kotlin代码问题了。常见原因包括使用了下不存在的API、缺少依赖、JDK版本不匹配导致语法不兼容。这种报错一般也会直接指向具体文件路径和行号按提示定位就行。4.5 关于内存和Daemon进程的问题还有一种情况进程构建到一半突然报OutOfMemoryError或者Gradle Daemon disappeared。这是因为Gradle默认的JVM内存设置太小或者机器上其他进程占用了太多内存。处理方式是在gradleTemplate.properties里调大org.gradle.jvmargsorg.gradle.jvmargs-Xmx4096m -XX:MaxMetaspaceSize1024m如果你机器物理内存只有8G建议先关闭Unity里其他庞大的窗口再构建。千万不要直接拉到-Xmx8192m机器如果顶不住反而会触发系统级OOM那就要重启电脑了。5. 常见报错信息与解决方案速查表为了省得你反复翻日志我把这些年遇到的典型报错整理成一张速查表按出现频率排序。建议收藏起来下次打包遇到同款报错直接对着查报错关键字真正原因解决办法Failed to find target with hash string android-XXSDK缺少对应平台在SDK Manager中安装对应的platforms;android-XXCould not resolve com.android.tools.build:gradle:X.X.X无法访问Google Maven仓库配置阿里云镜像仓库或检查网络访问Could not download gradle-X.X.X-all.zipGradle发行版下载失败手动下载对应zip放入wrapper缓存目录或用镜像地址Duplicate class androidx.*依赖冲突新旧support包混用开启AndroidX/Jetifier升级或替换冲突库SDK component build-tools;XX.X.X is missingSDK缺少构建工具在SDK Manager中补装对应build-toolsNDK not configuredNDK缺失或路径不正确用Unity Hub装匹配的NDK版本检查Preferences里的路径Execution failed for task :launcher:mergeReleaseResources资源文件冲突或不合法检查res目录命名、去掉特殊字符、排查aar资源冲突Execution failed for task :launcher:processReleaseManifestManifest文件有错误按日志提示修改Assets/Plugins/Android/AndroidManifest.xmlUnsupported class file major version XXJDK版本过新改用Unity匹配的JDK版本java.lang.OutOfMemoryErrorGradle JVM内存不足调大org.gradle.jvmargs的Xmx值Permission denied或File not found杀毒软件或云同步工具锁定了构建文件把Unity工程目录和.gradle目录加入杀毒白名单关闭云同步这张表算是浓缩了大部分“经典”报错但实际项目中还有不少奇葩情况比如同一个错误在不同Unity版本里解决办法完全不一样。遇到这种情况不要死磕去查你当前Unity版本对应的Release Notes很多已知问题官方都给了详细说明和Workaround。6. 进阶优化让打包又快又稳的几点实践经验6.1 用自定义模板维护一个稳定构建环境前面提到过Custom Gradle Template我在实际项目中基本一直处于开启状态。开启之后Assets/Plugins/Android/下会生成baseProjectTemplate.gradle、gradleTemplate.properties、launcherTemplate.gradle、mainTemplate.gradle这四个文件。这四件套能让你精准控制仓库地址、依赖版本、Gradle JVM参数等配置而且在Unity升级或团队协作时这些配置会跟着工程走不会动不动就丢。不过要小心自定义模板意味着你要对自己的改动负责。比如我见过有人手动把gradle-6.1.1改成gradle-7.0.2结果AGP版本不兼容新的Gradle特性反而带来一堆新报错。结论是模板文件属于高级玩法保持“做最小修改”原则不要无缘无故升级版本号。6.2 项目路径与文件权限的隐形坑关于路径我真的要再说一遍Unity项目路径绝对不能有中文不能有空格也不建议放在桌面。Gradle工具链和Android SDK在处理带中文的路径时会出现各种“无法解读”的诡异报错。比如Invalid escape sequence或者Path too long有时候错误信息甚至完全看不出来和路径有关。我已经见过太多次项目在D:\游戏开发\某某项目下构建失败换个纯英文路径就正常了的例子。另外云同步工具和杀毒软件也是隐藏杀手。我曾经排查过一个客户的项目报错是java.io.IOException: Failed to delete百思不得其解最后发现是坚果云同步锁定了一堆构建文件。把项目目录、Library、Temp、.gradle目录全部加入杀毒排除名单后问题立刻消失。如果你用了OneDrive、坚果云、Dropbox这类工具构建期间建议先暂停同步。6.3 每次构建后记录一个“构建档案”这是我想单独推荐的一个习惯每次成功打包后在项目里创建一个构建说明文档记录Unity版本、脚本后端、目标架构、Gradle模板内容、使用的SDK/NDK版本以及这次有没有特殊处理过什么东西。别嫌麻烦当项目过几个月后又要出包或者同事接手时这份档案能帮你省下几个小时甚至几天的排查时间。我自己的项目里就有一个BUILD_NOTES.md每次构建完成就更新一次。里面除了记录版本信息还专门列了一张“历史报错与处理方式”的表格相当于把前面那些排查经验固化下来。很多时候新来的同学遇到问题先翻这张表90%的情况都能直接找到答案。6.4 从“能打包”到“快速打包”最后再聊一个很多人忽略的点Unity生成Gradle工程的耗时是可以优化的。你可以在gradleTemplate.properties里开启构建缓存org.gradle.cachingtrue org.gradle.paralleltrue这两个参数对大型项目提速非常明显。org.gradle.paralleltrue能让多个模块并行构建org.gradle.cachingtrue可以复用上次构建的中间结果。不过要留意如果你的项目里存在不规范的构建脚本或者自定义Task并行构建可能会引入新的竞争条件遇到这种情况再单独关闭也不迟。如果你打完包想看看构建的完整过程可以在Build Settings里勾选Build Report这样Unity会生成一份详细的构建报告每个步骤耗时都一目了然对定位“为什么打包这么慢”也有帮助。从第一次面对“Gradle build failed”时的手足无措到现在能根据一条报错快速判断出问题方向这个过程其实就是不断踩坑、看日志、试方案积累出来的。环境问题优先查网络问题尽早配镜像模板文件保持克制每次构建留个档案做到这几点Gradle build failed大概率就只是个偶尔出现的小插曲了。希望这篇整理帮你少走点弯路。
返回列表