ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Gradle 8.13升级避坑全记录:AGP、JDK、Flutter兼容指南

Gradle 8.13升级避坑全记录:AGP、JDK、Flutter兼容指南 最近一段时间Android Studio 项目圈子里最热的话题就是 Gradle 版本更新尤其是 8.13 这个版本出来之后一批又一批的升级咨询冒出来。我也趁着一次存量项目重构的机会把所有工程的 Gradle 版本从 8.10 左右一路拔到了 8.13。本来以为只是一个平平无奇的小版本升级结果从 IDEA 弹窗到 Flutter 项目构建再到 assembleDebug 卡死一路踩了不少坑。这篇就把我在 Gradle 8.13 上实际遇到过的问题、报错原文、排查思路和最终解决办法完整写出来给准备升级或正在升级的朋友做个参考。要说明的是这不是从官方文档里抄出来的“标准答案”而是我在真实项目里一步步试出来的。包括 Flutter 项目升级 Gradle 8.13 后出现的you are applying flutters 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.2Gradle 一下子跳到 8.13AGP 某些内部实现会调用 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 21Android Studio 内置的 JBRKotlin 1.9.24。这个组合在原生 Android 项目上跑得很稳。如果你的项目还在用 JDK 11Gradle 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并且只改版本号其他字段保持原样distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBaseGRADLE_USER_HOME zipStorePathwrapper/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 11Gradle 8.13 在配置阶段就会挂掉IDE 检测到不匹配于是把这个选择框弹出来让你重新指定。解决办法其实不复杂在弹窗里选Embedded JDKAndroid Studio 自带通常是 21或者在列表里手动加入你本机已安装的 JDK 17/21 路径然后点 OK。之后等 Gradle Sync 跑完弹窗就不会再出现。有一个比较容易误会的点这个弹窗只负责设置“Gradle 运行时的 JDK”它和你项目代码里的sourceCompatibility、targetCompatibility不是一回事。前者决定 Gradle 进程本身跑在哪个 Java 版本上后者决定最终编译出来的 class 文件字节码目标版本。两者可以不同但建议顺着来项目源码兼容级别设 17Gradle 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.homeC:/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.dirC\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk2.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固定成 17Kotlin 编译参数也同步设成 17。这样构建行为最可控不会因为你换了一台机器 JDK 版本不同就让构建结果发生变化。3. Flutter 项目升级后的经典报错imperatively using the apply script3.1 报错原文拆解这真的不是 Gradle 8.13 的“bug”我用热词搜索时发现很多人都在搜一段报错You are applying Flutters main Gradle plugin imperatively using the apply script.这个报错很容易被误认为“Gradle 8.13 有问题”其实它是 Flutter 项目结构落后于时代导致的。报错完整场景一般是一个 Flutter 工程跑flutter build apk或让 Android Studio 同步时构建失败日志里出现You are applying Flutters 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-cachetrue那么很可能会遇到一类新的坑你项目里自定义的 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.problemswarn设置上让 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改成distributionUrlfile\:///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.13Build 里还是 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报错几乎全部消失了。如果你在升级过程中遇到类似麻烦不要急着回退版本先看看项目结构是不是已经过时很多时候问题从源头就埋下了。
返回列表