☰
Gradle 8.13升级避坑全记录:AGP、JDK、Flutter兼容指南
2026/10/2 20:38:58 网站建设 项目流程

最近一段时间,Android Studio 项目圈子里最热的话题就是 Gradle 版本更新,尤其是 8.13 这个版本出来之后,一批又一批的升级咨询冒出来。我也趁着一次存量项目重构的机会,把所有工程的 Gradle 版本从 8.10 左右一路拔到了 8.13。本来以为只是一个平平无奇的小版本升级,结果从 IDEA 弹窗到 Flutter 项目构建,再到 assembleDebug 卡死,一路踩了不少坑。这篇就把我在 Gradle 8.13 上实际遇到过的问题、报错原文、排查思路和最终解决办法完整写出来,给准备升级或正在升级的朋友做个参考。

要说明的是,这不是从官方文档里抄出来的“标准答案”,而是我在真实项目里一步步试出来的。包括 Flutter 项目升级 Gradle 8.13 后出现的you are applying flutter's main gradle plugin imperatively using the apply script那种大坑,以及“运行 Gradle 任务 assembleDebug 时一直转圈”这类不会报错却更折磨人的问题。内容既有新老项目结构迁移,也有 JDK、AGP、Gradle 三者的版本纠缠,适合那些卡在升级路上的 Android 工程师、Flutter 开发者,以及刚接触 Android Studio 但已经被各种配置搞晕的新手。

1. 升级到 Gradle 8.13 前,这些前置版本差异先补课

1.1 不是只改 gradle wrapper 就能完事:Android Studio、AGP、Gradle、JDK 的四方关系

很多朋友一听到“把 Gradle 升到最新版”,第一反应就是打开gradle-wrapper.properties,把distributionUrl后面的版本号改成 8.13,然后点一下 Sync。这么干有时候能成功,更多时候只会换来一排排红色报错。

先说清楚一个概念:Gradle 不是 Android 项目的编译工具,而是构建系统的骨架。Android 项目里真正负责打包、生成 APK、资源合并、Dex 编译这一堆逻辑的是 AGP,也就是 Android Gradle Plugin。AGP 和 Gradle 是分开发布的,各自有自己的版本号,而且 AGP 对 Gradle 有一个“最低版本要求”。

Gradle 8.13 本身只是一个构建框架更新,但新版框架意味着 AGP 也需要足够新的版本才能兼容。如果你项目里的 AGP 还停留在 8.1、8.2,Gradle 一下子跳到 8.13,AGP 某些内部实现会调用 Gradle 已经不推荐甚至已经移除的 API,跑起来就会报各种找不到方法、找不到类、Unsupported class file major version之类的错。

我建议把几个关键版本先想清楚,再动手改:

角色负责什么版本关系
Android StudioIDE 壳子,提供编辑和检查功能对 AGP 有最低版本限制
AGP完成 Android 打包的插件对 Gradle 有最低版本要求
Gradle构建框架本身对 JDK 有最低版本要求
JDK所有 Java/Kotlin 代码的编译运行环境版本太低时 Gradle 直接起不来

我这次升级时,用的组合参考如下,给大家做个参照:Gradle 8.13、AGP 8.7.2、JDK 21(Android Studio 内置的 JBR),Kotlin 1.9.24。这个组合在原生 Android 项目上跑得很稳。如果你的项目还在用 JDK 11,Gradle 8.13 基本不会理你,直接提示需要更高版本的 JDK。

1.2 wrapper 与发行版的坑:distributionUrl 到底该指向哪里

gradle-wrapper.properties是 Gradle 升级的第一道门。这里的distributionUrl有两种常见写法:gradle-8.13-bin.zip和gradle-8.13-all.zip。

-bin是二进制发行版,只包含运行 Gradle 所需的编译好的代码,体积小,日常构建完全够用;-all额外带源码和文档,体积大不少,主要是给开发 Gradle 插件或查看 Gradle 源码的人用的。CI、普通项目构建没必要用-all,下载慢还占磁盘。

另一个容易踩的坑是:不要直接用 Android Studio 里“右键项目 > Gradle > 选择 Gradle Version”这种可视化方式去切distributionUrl。它有时候会把发行版类型改掉,或者给 URL 加一些本地路径,导致同一份工程在不同机器上行为不一致。我习惯手动编辑gradle-wrapper.properties,并且只改版本号,其他字段保持原样:

distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists

这里还有个细节:如果你用的是 Android Studio 自带的 Gradle 而不是 wrapper 里的版本,IDE 每次打开项目都可能重新下载对应的发行版到用户目录的wrapper/dists下。如果公司网络环境下载官网发行版很吃力,可以考虑把发行版包放到内网或本机,再把 URL 改成file:///D:/gradle-dist/gradle-8.13-bin.zip这种本地路径。这个办法尤其适合需要统一离线分发环境的团队。

2. 最容易把新手卡死的 JDK 选择提示与 Daemon 启动问题

2.1 "Select the Java Development Kit (JDK) you want Gradle to use" 弹窗的真实含义

升级到 Gradle 8.13 后,Android Studio 大概率会弹出一个对话框,标题大致是:Select the Java Development Kit (JDK) you want Gradle to use when building your project。界面里会有几个选项,让你选 Embedded JDK 或 Local JDK。

这个弹窗的含义很简单:Gradle 在启动时需要确定一个 JDK 来运行自身。8.13 这个版本对 JDK 的最低要求是 17,推荐使用 21;如果项目之前用的是 Android Studio 老版本自带的 JDK 11,Gradle 8.13 在配置阶段就会挂掉,IDE 检测到不匹配,于是把这个选择框弹出来让你重新指定。

解决办法其实不复杂,在弹窗里选Embedded JDK(Android Studio 自带,通常是 21),或者在列表里手动加入你本机已安装的 JDK 17/21 路径,然后点 OK。之后等 Gradle Sync 跑完,弹窗就不会再出现。

有一个比较容易误会的点:这个弹窗只负责设置“Gradle 运行时的 JDK”,它和你项目代码里的sourceCompatibility、targetCompatibility不是一回事。前者决定 Gradle 进程本身跑在哪个 Java 版本上,后者决定最终编译出来的 class 文件字节码目标版本。两者可以不同,但建议顺着来:项目源码兼容级别设 17,Gradle JVM 也选 17 或 21,避免出现“Gradle 能跑但编译出的字节码和依赖库不兼容”的怪问题。

2.2 两处 JDK 配置要一致:IDE 设置与 gradle.properties 的冲突

除了弹窗,Gradle 还有两个地方可以配置 JDK 路径。一个是 Android Studio 的Settings > Build Tools > Gradle > Gradle JDK,另一个是gradle.properties里的org.gradle.java.home。

这两处一旦同时配置但路径不一致,以org.gradle.java.home为准。很多人的迷之操作是:在 IDE 设置里换了 Gradle JDK,项目重新 Sync 后还是提示旧 JDK 报错,查了半天发现是gradle.properties里早有人写上org.gradle.java.home=C:/Program Files/Java/jdk-11,于是 IDE 窗口里怎么改都没用。

所以我建议团队项目里不要把org.gradle.java.home写进gradle.properties。不同开发者的 JDK 安装路径千差万别,写死之后要么别人拉下来跑不了,要么大家都被这份配置锁死在某个版本上。正确做法是:如果不是特殊需求,让 Android Studio 的 Gradle JDK 设置自己决定即可;如果团队成员结构不统一,最好在 README 里约定统一使用 JDK 17 或 21。

顺便说一下local.properties里的sdk.dir。升级过程中如果出现“SDK location not found”之类的提示,检查一下项目的local.properties里有没有写对 Android SDK 路径。Windows 下路径里的反斜杠要转义成双反斜杠,例如:

sdk.dir=C\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk

2.3 Java Toolchain 的陷阱:自动探测与自动下载

Gradle 8.13 对 Java Toolchain 的支持已经很成熟了。Toolchain 的意思是在构建脚本里直接声明“我要用 Java 17 来编译”,Gradle 会自动探测本机有没有对应 JDK,如果没有,甚至可以自动下载一个。

这个机制看着方便,但坑也在这里。过了依赖仓库之后,Gradle 自动下载 JDK 需要访问特定站点,公司内网环境很可能被拦。而且 AGP 本身对 Toolchain 的支持方式和纯 Java 项目不太一样:在 Android 项目里用java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }这种写法,在某些 AGP 版本下会和 Android 插件的 Java 编译配置打架,反而导致Could not determine java version from '17.0.1'之类的问题。

我的建议是:Android 项目里尽量别用 Toolchain 自动探测,老老实实在android块里写compileOptions,把sourceCompatibility和targetCompatibility固定成 17,Kotlin 编译参数也同步设成 17。这样构建行为最可控,不会因为你换了一台机器 JDK 版本不同就让构建结果发生变化。

3. Flutter 项目升级后的经典报错:imperatively using the apply script

3.1 报错原文拆解:这真的不是 Gradle 8.13 的“bug”

我用热词搜索时,发现很多人都在搜一段报错:You are applying Flutter's main Gradle plugin imperatively using the apply script.这个报错很容易被误认为“Gradle 8.13 有问题”,其实它是 Flutter 项目结构落后于时代导致的。

报错完整场景一般是:一个 Flutter 工程,跑flutter build apk或让 Android Studio 同步时,构建失败,日志里出现:

You are applying Flutter's main Gradle plugin imperatively using the apply script. Remove the `apply from:` call and instead use the standard plugin mechanism.

这句话的意思是:你的项目在build.gradle里用了老式脚本方式导入 Flutter Gradle 插件,比如:

apply from: "$flutterRoot/packages/flutter_tools/gradle/app_plugin_loader.gradle"

而新版 Flutter 工具链和 Gradle 8.13 环境下,官方要求用标准插件声明方式替代这种命令式apply from。这不是报错,而是提示你迁移到新的模板结构。

3.2 完整排查链路:从根 build.gradle 到 settings.gradle.kts

遇到上面的报错,不要慌,也别直接回退 Gradle 版本。按下面的顺序排查,基本都能解决。

第一步,确认 Flutter 版本。flutter --version,如果 Flutter 3.16 及以上,项目模板默认就是新结构,需要迁移。

第二步,看根目录build.gradle是不是长这样的老结构:

buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.1.0' } } allprojects { repositories { google() mavenCentral() } }

同时根目录下还有设置脚本settings.gradle,里面没有pluginManagement。这些都意味着工程还停留在老一代模板。

第三步,对照 Flutter 新项目模板。新建一个 Flutter 项目,打开它的settings.gradle.kts,会看到类似这样的内容:

pluginManagement { val flutterSdkPath = run { val properties = java.util.Properties() file("local.properties").inputStream().use { properties.load(it) } val flutterSdkPath = properties.getProperty("flutter.sdk") require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } flutterSdkPath } includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { google() mavenCentral() gradlePluginPortal() } } plugins { id("dev.flutter.flutter-gradle-plugin") version "1.0.1" apply false } include(":app")

第四步,把老工程向这个结构靠拢。根build.gradle里那些buildscript、allprojects、classpath配置,能删就删,因为现在插件依赖统一放到settings.gradle.kts的pluginManagement里管理。app/build.gradle也要从 Groovy 迁移成 Kotlin DSL,并且把插件声明改为:

plugins { id("com.android.application") id("dev.flutter.flutter-gradle-plugin") id("org.jetbrains.kotlin.android") }

注意:dev.flutter.flutter-gradle-plugin这个插件 ID 不需要写版本号,因为已经在上层settings.gradle.kts里通过includeBuild引入了。

第五步,把原来的local.properties里flutter.sdk配置保留好,settings.gradle.kts里读取这个属性来定位 Flutter SDK 路径。不要直接在build.gradle里写def flutterRoot = localProperties.getProperty('flutter.sdk')这种老代码了。

迁移完成后,重新打开工程、执行 Sync,报错就会消失。

3.3 迁移到 Kotlin DSL 时容易被忽略的细节

老项目迁移不只是“抄新模板”那么轻松,有几个细节特别容易出错。

第一,android块里的 Flutter 属性写法变了。老 Groovy 写法可能是:

flutter { source = "../.." }

在 Kotlin DSL 里,直接写flutter { source = "../.." }有时会报“Unresolved reference”,因为 Flutter 插件的 Groovy DSL 属性和 KTS 不完全对应。比较稳妥的做法是参考新版 Flutter 生成的模板,把这个配置挪到settings.gradle.kts中通过includeBuild解决,app/build.gradle.kts里不再需要手写flutter {}块。如果还需要读取flutter.minSdkVersion、flutter.ndkVersion这类属性,可以直接通过flutter扩展对象获取,但如果你的 Flutter 版本比较新,模板里通常已经写好了默认值。

第二,ndkVersion在 Kotlin DSL 里的写法通常是ndkVersion = flutter.ndkVersion,注意它是赋值不是等号加引号。

第三,迁移后可能出现Configuration file settings.gradle和settings.gradle.kts同时存在的情况。Gradle 遇到这种会直接报“不能同时使用两种 settings 文件”。记得把旧的.gradle文件删干净,只保留.kts版本。同样,app/build.gradle和app/build.gradle.kts也不能并存。

第四,迁移完一定要删掉根目录和app目录下的build文件夹,还有项目的.gradle文件夹,否则老构建缓存会和新插件结构打架,出现一些莫名其妙的残留问题。清理命令我用的是:

./gradlew clean

如果还不行,就手动删除项目根下的.gradle和build目录。

4. 构建过程卡顿、缓存错误与依赖下载的坑

4.1 "Running Gradle task 'assembleDebug'..." 卡住不动时,怎么定位是哪个任务堵了

Android Studio 底部 Build 窗口长时间显示Running Gradle task 'assembleDebug'...,是升级后特别常见的一种状态。它看起来像没死,也没有具体报错,就是一直转圈。很多人的第一反应是“Gradle 8.13 真垃圾”,其实这里面藏着好几种完全不同的原因。

我遇到的情况大体分三类:第一,依赖下载卡住了,Gradle 正在静默下载某个 jar 或 aar,因为网络问题一直超时重试,界面不显示进度;第二,Kotlin 编译阶段阻塞,Kotlin daemon 占用内存过高或进程僵死;第三,项目配置阶段就有死循环或非常耗时的任务,只是 Gradle 不打印出来。

快速定位方法很简单:不用 Android Studio,直接开命令行,在项目根目录执行:

./gradlew assembleDebug --info

--info会在控制台打印当前执行到的任务。如果你看到某一行卡住,比如Downloading https://...,那就是网络下载问题;如果卡在Executing task ':app:compileDebugKotlin',那就是 KMP/Kotlin 编译问题;如果连Configuration阶段都没过去,那基本是脚本或插件解析问题。

针对不同原因,处理方式不太一样:

  • 网络下载卡住:配置国内镜像仓库,或临时把distributionUrl改用本地离线包;依赖层面可以把 Google 和 Maven Central 放在repositories里,再配置阿里云镜像或者其他内网源。
  • Kotlin 编译卡住:打开gradle.properties,适当调大org.gradle.jvmargs,比如-Xmx4096m;同时给 Kotlin daemon 单独的 JVM 参数。
  • 配置阶段卡住:先用./gradlew --stop停掉所有 Gradle daemon,再清理.gradle/daemon缓存,重启 Android Studio,很多时候只是因为 daemon 状态脏了。

4.2 Configuration Cache 启用后的自定义任务报错

Gradle 8.x 一直在推行 Configuration Cache,简单说就是尽量缓存配置阶段的计算结果,让下一次构建略过配置脚本的执行。8.13 版本对这项功能的支持已经相当成熟,如果你在gradle.properties里开了:

org.gradle.configuration-cache=true

那么很可能会遇到一类新的坑:你项目里自定义的 Task,尤其是那些在配置阶段直接访问Project对象、file()、exec()的写法,在开启配置缓存后会被 Gradle 判定为“不兼容”,然后报类似Problem configuring task或invocation of 'Task.project' at execution time is unsupported的错误。

这个并不是 8.13 故意搞事情,而是 Gradle 希望你在自定义 Task 时用更规范的 API:把输入输出用@Input、@OutputFile声明,文件操作放到doLast里,避免在任务字段初始化时直接做 IO。如果一个项目用了很多上古写法,一下子开配置缓存会炸出一大片报错。

我当时的排查链路是:先在命令行用--no-configuration-cache跑一遍,验证是不是配置缓存导致的问题;确认是之后,逐一把不兼容的自定义任务改成在doLast中执行文件操作。如果工程特别大、时间紧,也可以先把org.gradle.configuration-cache.problems=warn设置上,让 Gradle 只警告不中断,先保证能构建出包,后续再逐步修。这是最务实的过渡方案。

4.3 依赖下载慢?离线包与本地仓库的落地玩法

网上搜“Gradle 离线包”的人很多,其实大家要找的通常是两类东西:一是 Gradle 发行版压缩包,也就是gradle-8.13-bin.zip对应的文件;二是项目依赖的 aar/jar 缓存,也就是GRADLE_USER_HOME/caches/modules-2里那一堆文件。

第一类离线包最简单,把gradle-8.13-bin.zip下载到本地,gradle-wrapper.properties改成:

distributionUrl=file\:///D\:/gradle-dist/gradle-8.13-bin.zip

注意 Windows 下冒号和反斜杠要转义。这样项目不管在什么机器上打开,只要这个文件还在本地路径,就不会去外网重新下载 Gradle。缺点是换了电脑或者CI环境,路径可能对不上,所以团队里一般放到共享盘或内网服务上。

第二类依赖缓存,适合断网环境复现构建。直接把一台已经成功构建过的机器上的GRADLE_USER_HOME/caches/modules-2整个拷贝到目标机器的相同位置,然后构建时加--offline:

./gradlew assembleDebug --offline

这个办法在完全断网的局域网演示环境里非常管用。但要小心,--offline模式会连根 Gradle 的插件下载也拦截,如果目标机器本地缓存里缺了某个插件版本,报错会比正常模式难查得多。我建议在断网环境里先用完整缓存跑通一次增删改,确认所有依赖都已缓存,再启用--offline。

5. 升级完后的验证清单与回头建议

5.1 一次干净构建需要检查的配置清单

从 Gradle 8.13 的坑里爬出来,不代表飞船已经安全着陆。我每次升级完都会按下面的清单过一遍,能省下很多反复排查的时间。

检查项推荐值 / 操作排查价值
gradle-wrapper.properties版本gradle-8.13-bin.zip确认不是 all 包,URL 没有串改
Gradle JDKJDK 17 或 21,推荐 Embedded JDK消除弹窗和 daemon 版本报错
gradle.properties里的org.gradle.java.home不建议使用,删掉避免覆盖 IDE 设置引发困惑
AGP 版本与 Gradle 8.13 兼容的最小版本避免底层 API 调用报错
settings.gradle.kts确认 Flutter 项目使用pluginManagement杜绝apply from报错
local.propertiessdk.dir、flutter.sdk路径正确避免 SDK 路径找不到
org.gradle.jvmargs至少-Xmx4096m,最好带编码参数减少 Kotlin 编译内存不足和乱码问题
org.gradle.paralleltrue多模块项目显著提速
android.useAndroidXtrue老项目不设置会报支持库冲突

还有一个很容易忘的坑:改了gradle-wrapper.properties后,一定要让 Android Studio 重新加载 Gradle 项目。在工具栏找到File > Sync Project with Gradle Files,等它重新解析完,再跑构建。否则 IDE 内部还是会引用旧版本,形成“我明明改了 8.13,Build 里还是 8.10”的错觉。

5.2 新旧项目各自的升级策略

如果你的项目是刚用 Android Studio 模板新建的,那恭喜你,Gradle 8.13 配套的模板基本都适配好了,直接构建就行。这类项目最容易出的问题其实是 Android Studio 版本太旧,内置的 Gradle 插件解析器和新的 Gradle 版本不配套。遇到这种情况,优先升级 Android Studio 到最新稳定版,而不是去降 Gradle。

如果你的项目是维护了三五年的存量工程,升级策略要更保守一点:不要一步到位从 Gradle 7.x 跳到 8.13,中间跨度太大,报错会混在一起,根本分不清是哪个组件引起的。我推荐按“AGP 先行、Gradle 跟进”的顺序,每次只升一个大版本,确保每一步都能正常 Sync 和出包。

具体到本次 Gradle 8.13,我的体会是:它本身构建速度和稳定性都不错,真正让人头疼的大多是存量工程没有跟上 Flutter、AGP、KTS 这几条主线的发展。在我把 Flutter 老工程迁到新模板后,再次构建时,之前那些apply script报错几乎全部消失了。如果你在升级过程中遇到类似麻烦,不要急着回退版本,先看看项目结构是不是已经过时,很多时候问题从源头就埋下了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询