解决Unity Gradle构建APK时的JAVA_TOOL_OPTIONS与TLS协议冲突问题

📅 发布时间:2026/7/8 12:55:48 👁️ 浏览次数:
解决Unity Gradle构建APK时的JAVA_TOOL_OPTIONS与TLS协议冲突问题
1. 问题初现那个让人头疼的构建失败日志那天下午我正像往常一样在Unity里点击“Build And Run”准备把新功能打包成APK在真机上跑一下看看效果。结果Unity Editor底部的控制台突然就红了刷出来一大片错误日志。我当时心里就“咯噔”一下心想“完了又踩坑了。”错误信息的核心就是开头那几行stderr[ Picked up JAVA_TOOL_OPTIONS: -Dfile.encodingUTF-8 FAILURE: Build failed with an exception. * What went wrong: A problem occurred configuring project :launcher. Could not resolve all artifacts for configuration :launcher:classpath. Could not resolve com.android.tools.build:gradle:7.1.2. Could not get resource https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/7.1.2/gradle-7.1.2.pom. Could not GET https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/7.1.2/gradle-7.1.2.pom. The server may not support the clients requested TLS protocol versions: (TLSv1.2, TLSv1.3). You may need to configure the client to allow other protocols to be used.相信很多Unity开发者尤其是刚开始接触Gradle构建或者刚从Internal旧版构建系统切换过来的朋友都见过这个报错。它看起来有点复杂但核心矛盾其实就两个一个是开头的JAVA_TOOL_OPTIONS环境变量另一个是结尾的 TLS 协议版本不匹配。简单来说就是你的构建环境通过JAVA_TOOL_OPTIONS设置了一些参数在尝试从谷歌的服务器下载Gradle插件时双方在“握手”的加密协议版本上没谈拢导致连接被终止构建自然就失败了。这个问题的典型场景是你可能在系统环境变量、用户环境变量或者某个启动脚本里设置了JAVA_TOOL_OPTIONS目的是为了解决中文路径、文件编码等问题。这个变量本身没错但它会影响所有Java进程包括Unity启动的Gradle构建进程。而Gradle在构建时需要从网络仓库比如dl.google.com下载依赖这个网络请求的底层是Java的网络库发起的。当JAVA_TOOL_OPTIONS中可能包含某些与安全协议相关的隐式设置或者与你本地Java运行环境JRE/JDK的版本、Gradle版本、以及远程服务器的安全策略产生交互时就可能引发TLS握手失败。对于刚接触这个问题的朋友可以把它想象成你Gradle想用一套特定的暗号TLSv1.2/1.3去跟一个仓库管理员谷歌服务器对话取货但你身上挂了个牌子JAVA_TOOL_OPTIONS这个牌子可能让管理员觉得你的暗号方式不安全或者不被支持于是他直接拒绝和你交易。2. 抽丝剥茧理解JAVA_TOOL_OPTIONS与TLS的恩怨情仇要解决这个问题我们不能光会照着别人的代码改得稍微明白一点背后的原理这样下次再遇到类似的网络或环境问题你也能自己排查。咱们先拆开看看这两个“主角”到底是怎么回事。JAVA_TOOL_OPTIONS 是个啥你可以把它理解成Java虚拟机JVM的一个全局“启动参数预设器”。只要系统里定义了这个环境变量那么任何新启动的Java程序包括Gradle因为Gradle本身就是一个Java应用都会自动把这些参数加载进去。最常见的设置就是-Dfile.encodingUTF-8这是为了解决在中文Windows系统下Java程序读写文件可能出现的乱码问题。这个初衷是好的但问题在于它影响的层面太广了。除了文件编码它还可能被用来设置其他JVM参数比如网络代理、安全策略文件路径、甚至是一些调试参数。当Unity调用Gradle进行构建时Gradle进程会继承这个环境变量里面所有的JVM参数都会生效。TLS协议又是什么TLS传输层安全协议你可以理解为互联网上数据加密传输的“安全信封”标准。当你的Gradle试图从https://dl.google.com...这样的地址下载插件时它首先要和服务器进行一次“TLS握手”双方协商出一个共同的、安全的加密方式来保护后续的下载数据。TLS本身有多个版本比如TLSv1.2、TLSv1.3版本越新通常越安全。但是服务器和客户端你的Gradle必须支持至少一个相同的版本握手才能成功。冲突是怎么发生的关键点来了JAVA_TOOL_OPTIONS里可能并没有直接设置TLS版本但它会影响JVM的默认行为。在某些Java版本特别是较旧的版本比如Java 8的某些早期更新或特定的JVM安全配置下默认启用的TLS协议列表可能不包含服务器所要求的版本或者与服务器的安全策略冲突。更常见的一种情况是JAVA_TOOL_OPTIONS可能通过-Dhttps.protocols或-Djdk.tls.client.protocols这样的参数显式地限定了Gradle只能使用某几个TLS协议。如果这个限定列表里没有服务器支持的协议握手就会失败报出“The server may not support the clients requested TLS protocol versions”的错误。另一种可能是环境变量里设置了网络代理Proxy相关参数但这些代理配置不正确或者与TLS握手过程产生了干扰。所以问题的链条是这样的系统环境变量JAVA_TOOL_OPTIONS被设置 - Unity启动Gradle构建进程 - Gradle继承该变量中的JVM参数 - 这些参数影响了Gradle的JVM的网络和安全配置 - 当Gradle尝试从谷歌仓库下载依赖时TLS握手失败 - 构建中止。理解了这个链条我们的解决思路就很清晰了要么让JAVA_TOOL_OPTIONS不影响Gradle要么调整它的内容确保TLS握手能成功。3. 实战解决从临时清除到永久修复的四种方法知道了病因咱们就来开药方。这里我根据问题的紧急程度和解决方式的彻底性给你梳理了四种方法你可以从方法一开始尝试如果不行再往后看。我个人推荐方法三或方法四因为它们能从根本上解决问题一劳永逸。3.1 方法一临时清除环境变量快速验证这是最快速、用于验证问题根源的方法。思路很简单在启动Unity或者执行构建命令的瞬间临时把JAVA_TOOL_OPTIONS这个环境变量给“屏蔽”掉。对于Windows用户如果你习惯用Unity Editor的Build按钮可以这样操作找到你的Unity快捷方式桌面或开始菜单。右键点击选择“属性”。在“快捷方式”标签页找到“目标”输入框。它可能类似C:\Program Files\Unity\Hub\Editor\2022.3.20f1\Editor\Unity.exe。在引号结尾后面先加一个空格然后输入以下命令-env JAVA_TOOL_OPTIONS整个“目标”框的内容看起来会是这样C:\Program Files\Unity\Hub\Editor\2022.3.20f1\Editor\Unity.exe -env JAVA_TOOL_OPTIONS点击“应用”和“确定”。关闭所有Unity窗口然后从这个修改过的快捷方式启动Unity。这样启动的Unity进程及其所有子进程包括Gradle都不会继承到系统的JAVA_TOOL_OPTIONS变量。对于macOS/Linux用户或命令行构建如果你是通过命令行执行构建比如用Unity -batchmode -quit ...做CI/CD可以在命令前直接覆盖这个环境变量# 在终端中执行 JAVA_TOOL_OPTIONS /Applications/Unity/Hub/Editor/2022.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -quit -projectPath /Your/Project/Path -executeMethod BuildScript.BuildAndroid注意JAVA_TOOL_OPTIONS后面是空的等号紧接着就是空格或命令开始。验证结果用这种方式构建一次。如果构建成功了那就百分之百确认是JAVA_TOOL_OPTIONS环境变量引起的问题。但这只是一个临时解决方案每次都要通过特殊方式启动Unity太麻烦我们接下来要找更持久的方法。3.2 方法二在Gradle脚本中覆盖JVM参数推荐这是更优雅、更工程化的解决方案。我们不在系统层面折腾而是在Gradle构建脚本内部针对Gradle守护进程Daemon的启动参数进行覆盖。Unity项目在构建Android包时会生成或使用一个gradle.properties文件这个文件就是我们的“主战场”。操作步骤在你的Unity项目根目录下找到Assets/Plugins/Android文件夹。如果没有就创建一个。在Android文件夹内创建或编辑一个名为gradle.properties的文本文件。在这个文件里添加以下内容# 覆盖系统可能设置的 JAVA_TOOL_OPTIONS将其清空 systemProp.JAVA_TOOL_OPTIONS # 同时显式设置 Gradle Daemon 使用的JVM参数确保TLS协议 org.gradle.jvmargs-Dfile.encodingUTF-8 -Dhttps.protocolsTLSv1.2,TLSv1.3 -Djdk.tls.client.protocolsTLSv1.2,TLSv1.3保存文件。原理解释systemProp.JAVA_TOOL_OPTIONS这一行是关键。systemProp.前缀表示这是一个系统属性。在Gradle中通过systemProp.设置的属性其优先级高于从环境变量中读取的JAVA_TOOL_OPTIONS。我们把它设为空就相当于在Gradle内部“抹掉”了外部环境变量的影响。org.gradle.jvmargs这个参数是专门用来设置Gradle守护进程的JVM启动参数的。我们在这里重新定义了我们需要的东西-Dfile.encodingUTF-8把之前环境变量里可能有的正确配置解决乱码重新加回来确保文件操作正常。-Dhttps.protocolsTLSv1.2,TLSv1.3和-Djdk.tls.client.protocolsTLSv1.2,TLSv1.3这两个参数显式地告诉Gradle的JVM“请使用TLSv1.2和TLSv1.3协议去进行HTTPS连接”。这直接解决了与谷歌服务器的握手问题。这个方法的好处是它只影响当前项目的Gradle构建不会动你的系统环境干净又可控。Unity在构建时会自动将这个gradle.properties文件合并到生成的Gradle项目中。3.3 方法三修改Unity的Gradle模板文件一劳永逸如果你发现方法二在某些情况下不生效或者你的项目结构比较特殊那么直接修改Unity提供的Gradle模板是最彻底的方式。Unity允许我们自定义构建过程中用到的关键Gradle脚本。操作步骤在Unity Editor中打开Edit - Project Settings - Player。在Player Settings面板找到“Publishing Settings”区域旧版本Unity可能在“Other Settings”下面找“Build”子区域。勾选“Custom Base Gradle Template”和“Custom Gradle Properties Template”如果存在。勾选后Unity会在Assets/Plugins/Android目录下生成对应的模板文件。我们主要编辑baseProjectTemplate.gradle文件。用文本编辑器打开它。在这个文件的顶层通常在所有allprojects或buildscript代码块之外最开头的地方添加以下配置// 在文件顶部添加 allprojects { // 为所有子项目配置构建环境 afterEvaluate { project - // 清除可能干扰构建的环境变量影响 tasks.withType(JavaExec) { environment.remove(JAVA_TOOL_OPTIONS) } // 确保使用正确的TLS协议 project.ext.set(jvmArgs, [-Dhttps.protocolsTLSv1.2,TLSv1.3, -Djdk.tls.client.protocolsTLSv1.2,TLSv1.3]) } }同时确保gradle.properties文件或模板里也有方法二中提到的systemProp.JAVA_TOOL_OPTIONS设置。这个方法直接在Gradle的执行层面移除了环境变量并为所有Java执行任务包括下载依赖强制指定了TLS协议力度更强。3.4 方法四检查与更新Java运行环境治本之策有时候问题的根源可能在于你机器上安装的Java版本太旧。旧版本的Java比如非常老的Java 8 update可能默认不支持较新的TLSv1.3或者其默认的安全协议配置与当前互联网服务器的要求不匹配。检查你的Java版本打开命令行CMD或PowerShell输入java -version查看输出。确保你使用的是相对较新的版本比如Java 8 的较新更新建议 update 201 以后或者Java 11、Java 17等LTS版本。Unity官方通常推荐使用OpenJDK 8或11。更新或安装合适的JDK建议从可靠的来源下载安装包例如AdoptiumEclipse Temurin、Microsoft Build of OpenJDK等。安装完成后你需要配置系统的JAVA_HOME环境变量指向新JDK的安装目录例如C:\Program Files\Eclipse Adoptium\jdk-11.0.xx.xx-hotspot并将%JAVA_HOME%\bin添加到PATH变量中。同时检查并考虑删除或修改系统/用户环境变量中的JAVA_TOOL_OPTIONS。如果你设置它只是为了-Dfile.encodingUTF-8那么这个参数其实可以在别处如方法二更精确地设置。直接删除它可以避免很多未知冲突。重启命令行和Unity让新的环境变量生效。升级到新版本Java后其内置的TLS协议库通常更新、更全面与主流服务器的兼容性更好可能无需额外配置就能解决握手问题。4. 配置仓库镜像加速构建与规避网络问题解决了TLS握手问题构建时可能还会因为网络慢或连接不稳定而卡住。特别是从谷歌官方仓库下载依赖在国内网络环境下有时会很痛苦。一个非常实用的技巧是配置国内镜像仓库将下载源替换为阿里云、腾讯云等国内镜像站速度会快很多有时也能规避一些诡异的网络协议问题。Unity 2022及以上版本或者使用Gradle 7.0构建系统的项目其仓库配置主要在settingsTemplate.gradle文件中。你需要像原始文章里提到的那样修改这个模板文件。详细配置步骤在Unity Editor中确保在Player Settings - Publishing Settings下勾选了“Custom Gradle Settings Template”。这会在Assets/Plugins/Android下生成settingsTemplate.gradle文件。用文本编辑器打开这个文件。找到pluginManagement和dependencyResolutionManagement这两个部分的repositories闭包。你需要在这两处都添加国内镜像源。将镜像地址如阿里云Maven仓库添加到repositories列表的靠前位置。Gradle会按顺序查找仓库放在前面会优先使用。一个配置好的settingsTemplate.gradle文件关键部分示例如下pluginManagement { repositories { // 优先使用国内镜像 maven { url https://maven.aliyun.com/repository/public } maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/gradle-plugin } // 保留原始仓库作为后备 gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { // 优先使用国内镜像 maven { url https://maven.aliyun.com/repository/public } maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/gradle-plugin } // 保留原始仓库作为后备 google() mavenCentral() // Unity Android插件所需的flatDir仓库务必保留 flatDir { dirs ${project(:unityLibrary).projectDir}/libs } } } include :launcher, :unityLibrary重要提示flatDir仓库是Unity Android插件存放本地.aar或.jar文件的地方绝对不能删除否则会找不到Unity的核心库。配置镜像后Gradle会优先从阿里云仓库下载依赖如果镜像上没有比如一些非常新的或谷歌专属的库它会自动回退到google()和mavenCentral()去下载。这不仅能大幅提升构建速度减少因网络超时导致的失败在某些情况下镜像站的服务端配置可能更兼容你的客户端环境间接避免了TLS握手等问题。5. 避坑指南与深度排查技巧即使按照上面的方法操作了有时候可能还是会遇到一些“幺蛾子”。这里分享几个我踩过坑后总结的排查技巧和注意事项。清理Gradle缓存Gradle会把下载的依赖包缓存到本地。如果缓存的文件损坏或者旧的缓存与新的配置冲突也会引发奇怪的问题。当你修改了仓库地址或Gradle版本后最好清理一下缓存。缓存位置通常在用户主目录下的.gradle/caches文件夹Windows在C:\Users\你的用户名\.gradle\caches。直接删除这个caches文件夹是安全的但下次构建需要重新下载所有依赖会慢一点。更精准的做法是在命令行进入项目目录执行gradlew cleanBuildCacheWindows或./gradlew cleanBuildCachemacOS/Linux。检查防火墙和安全软件有些企业网络环境或个人电脑上的安全软件如某些杀毒软件或防火墙可能会深度检测或修改HTTPS流量这有可能干扰TLS握手。尝试临时禁用防火墙或安全软件在安全的环境下然后构建一次看是否成功。如果成功就需要在安全软件里为Javajava.exe、javaw.exe和Gradlegradlew.bat添加例外规则。使用命令行进行构建并查看详细日志Unity Editor的构建窗口输出的日志有时不够详细。你可以尝试使用Unity的命令行接口进行构建并捕获更详细的日志。在命令行中你可以设置环境变量并看到所有输出。例如在项目根目录执行路径需要替换成你的set JAVA_TOOL_OPTIONS C:\Program Files\Unity\Hub\Editor\2022.3.20f1\Editor\Unity.exe -batchmode -quit -logFile build.log -projectPath . -executeMethod YourBuildScript.BuildAndroid这会在当前目录生成一个build.log文件里面包含了从Unity启动到Gradle构建结束的所有详细信息搜索“TLS”、“SSL”、“handshake”、“Could not GET”等关键词能帮你定位到更具体的错误信息。关于Unity版本与Gradle版本的匹配不同的Unity版本默认使用或兼容的Gradle版本不同。你可以在Player Settings - Publishing Settings - Build区域看到或选择Gradle版本。一般来说使用Unity推荐的默认版本是最稳妥的。如果你手动升级了mainTemplate.gradle文件中的Gradle插件版本如com.android.tools.build:gradle:7.4.2请确保它与Gradle Wrapper版本gradle/wrapper/gradle-wrapper.properties中的distributionUrl以及你的JDK版本兼容。不兼容的组合也会引发一系列难以诊断的问题包括网络下载失败。最后保持耐心是关键。构建环境问题往往由多个因素叠加导致。按照从简到繁的顺序先尝试方法一验证问题然后用方法二或三进行项目级修复同时配置方法四的镜像加速并确保你的Java环境是较新且干净的。这套组合拳下来绝大多数由JAVA_TOOL_OPTIONS和 TLS 协议引发的构建失败问题都能被解决。记住每次修改配置后最好关闭Unity Editor并删除项目中的Library和Temp文件夹然后重新打开项目构建以确保所有更改都能被干净地加载。