资讯动态

AGP 9.0升级实战:Gradle 9迁移、Flutter混合工程与构建效率优化

发布时间:2026/9/16 3:37:43 来源:尧图企业网站定制
上个迭代我接了个任务把团队两个项目的构建工具链从 AGP 8.x 升到Android Gradle Plugin 9.0。任务本身不复杂复杂的是升完之后Flutter 那边的同事拿着报错截图找过来说主工程的 Gradle 插件被“强制式应用”了紧接着 CI 上又冒出一个tag number over 30 is not supported。前后折腾了两天我才把两条链路完全理顺也第一次真切感受到 9.0 这套工具链对构建效率的拉动。这篇文章就是那两天的完整记录把所有关键步骤、报错拆解、参数调整都列出来适合正在规划 AGP 9.0 升级尤其是多模块工程和 Flutter 混合工程的 Android 开发者参考。1. 升级到底值不值AGP 9.0带来的实质性变化1.1 内置 Kotlin 支持转为正式能力AGP 9.0 最值得关注的一点是内置 Kotlin 支持已经转正。过去我们的 Android 工程要正常编译 Kotlin 代码必须在构建链里同时挂上 Kotlin Gradle Plugin也就是 KGP而且 KGP 版本和 AGP 版本之间要小心翼翼地对齐版本不匹配就报一堆警告甚至直接失败。AGP 9.0 把这层外部依赖收编了。Kotlin 编译任务由 AGP 统一调度Kotlin 版本由 AGP 声明和管理配置阶段不再需要额外加载一套重量级插件。这意味着什么最直观的感受是配置阶段轻了。我之前维护过一个 50 多个模块的工程旧的构建链光是加载和配置 Kotlin 插件就要吃掉不少时间换到内置 Kotlin 后Task 图明显更简洁配置阶段从原来的十几秒降到了两三秒。但我不建议在升级当天就切到内置 Kotlin。KGP 虽然多了一层版本对齐成本但生态成熟像 kapt、kotlin-parcelize、Hilt 这类依赖 KGP 的插件升级节奏未必跟得上 AGP 9.0。稳妥做法是先让 AGP 9.0 配合外部 KGP 跑通稳定之后再评估是否移除外部 KGP、启用内置 Kotlin。1.2 Configuration Cache 与增量构建的叠加收益Gradle 9.0 把 Configuration Cache 的地位又抬了一级。配置缓存的作用简单说就是把整个配置阶段的计算结果缓存下来下次构建只要构建逻辑没变就直接复用不用重新执行所有脚本、重新计算任务图。这对持续集成环境尤其重要——CI 上每次都是干净工作区过去每次构建都要从零开始解析构建脚本浪费非常明显。AGP 9.0 对 Configuration Cache 的适配已经相当完整配合 Gradle 9.0 更友好的问题提示开启后的收益是实打实的。我在一个 56 模块的项目上做过对比关闭配置缓存时一次 clean 构建配置阶段要 18 秒开启后基本稳定在 2 秒左右。这 16 秒看起来不多但一天几十次本地构建加十几次 CI 构建累积起来非常可观。增量构建方面AGP 9.0 继续收紧对过时 API 的支持构建系统在判断任务输入输出时更精确不会因为某些老旧的 hack 写法被迫把大量任务标记为“不缓存”。这也是为什么有些团队升级后感觉“增量编译变快了不少”——不是编译器本身快了而是之前被迫重复执行的活现在能被正确地跳过了。1.3 那“50%”是怎么算出来的“构建效率提升 50%”这类数字不同人理解差距很大。我测下来的实际体感是本地 debug 冷构建时间能压缩到原来的六成左右release 构建因为 R8 全量模式的默认开启收益更明显。拆开看时间主要省在三块。第一块是配置阶段Configuration Cache 加内置 Kotlin 让配置耗时大幅下降。第二块是任务执行阶段Gradle 9.0 对任务依赖的解析更高效AGP 9.0 也砍掉了一批废弃的合并、转换任务。第三块是 R8 全量模式代码裁剪、资源收缩、优化同时进行省掉了兼容模式带来的额外处理和产物。必须泼一盆冷水如果你的项目只有一两个模块构建时间本来就只有二三十秒那 50% 的感知不会太强。这个数字在多模块、依赖复杂、构建基线很长的工程上才体现得最明显。所以升级前先留好基线数据后面我会细说。2. 动手前的版本对齐与环境检查2.1 版本对应关系先理清升级 AGP 从来不是只改一个版本号那么简单它下游连着 Gradle、JDK、Android Studio上游还压着一堆第三方插件。我习惯先列一张版本对应表确认每层组件都在合理范围内。组件升级前常见情况升级目标AGP8.7.29.0.xGradle8.99.1底线 9.xJDK1717 或 21推荐 21Android StudioLadybugOtter 2025.1.1Kotlin2.0.x 外部 KGP2.2.x或评估内置 Kotlin这里有个容易踩的误区AGP 9.0 要求 Gradle 9.x但很多老项目的 Gradle Wrapper 还停在 7.x 或 8.x。直接改 AGP 版本号会导致 Gradle 版本不满足要求构建直接报错而且报错信息往往指向不明确。建议先升级 Gradle再升 AGP避免把两个变量混在一起排查。JDK 方面AGP 9.0 最低要求 JDK 17Android Studio Otter 默认携带的 JDK 21 完全兼容。如果项目有较多构建脚本、字节码处理逻辑JDK 21 在内存占用和编译速度上都会稍好一些。注意统一全团队 JDK 版本不然同一个项目在不同机器上表现不一样。2.2 init.gradle 里容易拖后腿的旧配置init.gradle是个容易被忽略的坑。很多团队为了统一全局配置在~/.gradle/init.gradle或GRADLE_USER_HOME/init.gradle里写了不少逻辑比如全局仓库地址、JVM 参数、强制 Gradle 版本等。这些配置在旧版本下运行正常升级到 Gradle 9 后可能直接冲突。我排查过一个问题某台机器构建一直失败报错信息是依赖解析时仓库列表为空。查了半天最后发现是 init.gradle 里还写着已经关闭的过时仓库地址导致 google()、mavenCentral() 的解析顺序被打乱了。这种问题很隐蔽因为它只在特定机器上复现CI 和别的同事机器都正常。升级前建议把 init.gradle 扫描一遍。过时的仓库地址、强制设置的低版本工具链、全局覆盖的 JVM 内存参数都值得重新审视。如果你们的 init.gradle 只是做了简单的仓库配置保留google()和mavenCentral()就够了我见过一个最小化示例allprojects { repositories { google() mavenCentral() } }更推荐的做法是把仓库管理迁移到项目内的settings.gradle里用dependencyResolutionManagement声明仓库。init.gradle 只保留必要的全局逻辑项目隔离性更好排查问题也更容易。2.3 升级前必须留一份基线数据这一步很多人跳过但我强烈建议不要省。升级前的构建性能数据是你升级后评判“到底变快没有”的唯一依据。没有基线后面所有关于效率提升的结论都是拍脑袋。操作很简单在升级前跑一次完整构建记录两个数字总耗时和配置阶段耗时。建议用 Gradle 自带的--profile参数./gradlew --stop ./gradlew clean :app:assembleDebug --profile执行完会在build/reports/profile/下生成 HTML 报告里面有三个核心指标配置阶段耗时、依赖解析耗时、任务执行耗时。把这份报告保存下来升级完再跑一次同样的命令直接对比数字波动。需要注意跑基线时要把机器负载控制住浏览器、IDE 索引之类的尽量关掉同一台机器、同一时间段、多跑几次取中位数。CI 上的历史构建记录也留一份CI 环境更稳定对比参考价值甚至比本地更大。3. 分步迁移从 Wrapper 到 DSL 的节奏控制3.1 先把 Gradle Wrapper 升到 9.x升级 Gradle 最直接的方式是修改gradle-wrapper.properties里的 distributionUrldistributionUrlhttps\://services.gradle.org/distributions/gradle-9.1.0-bin.zip但这里有个隐藏问题老项目的gradle-wrapper.jar可能是旧版 Gradle 生成的版本太老的话它可能无法正确解析和执行新版 Gradle 的下载。这时候直接改 distributionUrl 容易卡在下载或校验阶段。更稳妥的方式是用本机已安装的 Gradle 重新生成 wrappergradle wrapper --gradle-version 9.1这个命令会同步更新gradle-wrapper.jar、gradle-wrapper.properties和gradlew脚本三件套保持一致。执行完先跑一遍./gradlew --version确认版本真的切过来了再进行下一步。升级 Gradle 后建议先执行一次./gradlew help或./gradlew tasks单纯验证构建脚本能被正常解析这时候不要混着改 AGP 版本保持单一变量出问题好定位。3.2 切 AGP 版本与 Kotlin 插件管理方式Gradle 版本确认没问题后才开始动 AGP。如果项目用的是plugins {}DSL改动位置通常在根目录的build.gradle或settings.gradle里// libs.versions.toml agp 9.0.1 kotlin 2.2.20如果用的是老式buildscript { dependencies { classpath com.android.tools.build:gradle:8.7.2 } }写法改成 9.0.x 也能用但我建议借这次机会迁移到plugins {}DSL 和 Version Catalog。理由不是“新写法更酷”而是 AGP 9.0 对老式buildscript写法的兼容性已经在收紧继续使用意味着后续每次升级都多一层风险。Kotlin 管理方式这里多说一句。如果暂时不启用内置 Kotlin就继续保持外部 KGP并把根目录所有模块的 Kotlin 版本统一到同一个版本上。最好通过 Version Catalog 的kotlin 2.2.20统一控制避免某个子模块单独指定旧版本导致依赖树里出现两份 Kotlin 编译器。升级的第一个提交只改 Gradle 和 AGPKotlin 保持不动等构建通过后再单独提交 Kotlin 升级。3.3 DSL 语法迁移清单AGP 升级过程中最磨人的其实是 DSL 语法的变化。AGP 9.0 对应的是 Gradle 9.x很多旧写法被移除或者语义变了遇到报错再一个个改效率很低。我整理了一份常见的迁移对照升级前先对照检查一遍自己的构建脚本能省掉不少反复试错的功夫。旧写法新写法 / 说明minifyEnabled trueisMinifyEnabled truepackagingOptions { exclude ... }packaging { resources { excludes ... } }applicationVariants.all { variant - ... }使用androidComponents { onVariants { ... } }新 APIbuildToolsVersion 30.0.2通常可以直接删除AGP 默认使用自带版本android.enableJetifiertrue确认项目已完全 AndroidX 化后移除老式compile/provided依赖配置统一改为implementation/compileOnlyminifyEnabled这个改动在 Kotlin DSL 和 Groovy DSL 下表现不一样Groovy 下部分版本还能用Kotlin DSL 下基本强制要求isMinifyEnabled。如果项目是 Groovy DSL 老脚本升级后建议尽早开始向 Kotlin DSL 迁移因为 AGP 后续版本对 Groovy DSL 的支持只会越来越弱晚迁不如早迁。还有一个容易忽略的点namespace必须在android块里显式声明这是 AGP 8.0 以来的硬性要求AGP 9.0 会直接报错。如果你之前依赖package属性自动推断 namespace这次升级必须补上。3.4 验证与回滚策略每改完一个环节就执行一次构建验证不要攒着一起验证。我建议分三次提交第一次只升 Gradle Wrapper第二次升 AGP 版本和 DSL 语法第三次处理 Kotlin 版本和内置 Kotlin 评估。三次提交独立可回滚哪一步出问题直接回退对应提交就行不用连带其他改动一起回退。构建验证的命令尽量保持简单./gradlew :app:assembleDebug --stacktrace如果工程有多个模块先构建核心 app 模块再逐步扩展到全量构建。直接在 50 个模块的工程上验证报错信息会被淹没在大量日志里定位效率极低。4. Flutter / 混合工程升级 AGP 9.0 的专属坑4.1 报错原文拆解imperatively using the apply scriptFlutter 混合工程升级 AGP 9.0 时很多人会遇到一个很典型的报错You are applying Flutters main Gradle plugin imperatively using the apply script method, which is not supported. Remove this apply script call and use the plugins block instead.这句话拆开看就清楚了。Flutter 的主 Gradle 插件也就是dev.flutter.flutter-gradle-plugin现在要求必须用声明式的plugins {}块来应用不再允许用老式的apply script或apply plugin方式。这是 Gradle 9.0 对命令式apply调用收紧限制的直接结果。出现这个报错的项目通常在android/settings.gradle或android/app/build.gradle里写了类似下面这样的旧写法// android/app/build.gradle apply plugin: com.android.application apply plugin: kotlin-android apply from: $flutterRoot/packages/flutter_tools/gradle/flutter.gradle这种写法在旧版本 Gradle 下能跑但 AGP 9.0 Gradle 9.0 的组合下Flutter 插件明确拒绝被命令式应用。4.2 从 apply script 迁移到 plugins DSL修复方式很直接把命令式apply改写成声明式plugins块。新版 Flutter 模板的标准做法是在android/settings.gradle里通过pluginManagement引入 Flutter Gradle 插件pluginManagement { def flutterSdkPath { ... } // 从 local.properties 读取 flutter.sdk includeBuild($flutterSdkPath/packages/flutter_tools/gradle) repositories { google() mavenCentral() gradlePluginPortal() } } plugins { id dev.flutter.flutter-gradle-plugin }然后在android/app/build.gradle里同样使用plugins块plugins { id com.android.application id kotlin-android id dev.flutter.flutter-gradle-plugin }这段改完后原来通过apply from传递进来的flutterRoot等全局变量就不能继续用了。如果构建脚本里有依赖这些变量的逻辑需要改成从local.properties读取flutter.sdk这是很多混合工程迁移时容易漏掉的一步。另外要注意 Flutter 版本不能太老。旧版 Flutter SDK 自带的 Gradle 插件本身可能就不兼容 Gradle 9.0升级前把 Flutter SDK 切到最新的 stable 分支能避免大量无谓的折腾。4.3 混合工程里更隐蔽的 Kotlin 版本冲突Flutter 工程的 Kotlin 版本管理比较特殊。Flutter 插件模块通常会声明自己需要的 Kotlin 版本如果主工程升了 AGP 9.0而某些插件模块还在用很老的 Kotlin Gradle Plugin构建时可能出现 Kotlin 编译器版本不一致的诡异问题。我的经验是混合工程不要急着切 AGP 内置 Kotlin先把外部 KGP 版本显式统一。具体做法是在根目录build.gradle里声明一个全局 Kotlin 版本所有子模块都引用同一个版本包括 Flutter 插件模块。如果你用的 Flutter 版本比较新插件模块已经通过pluginManagement统一管理了插件版本那 Kotlin 版本通常会跟随 Flutter 工具链自动对齐。如果还遇到版本冲突手段是从依赖树里定位./gradlew :app:dependencies --configuration debugRuntimeClasspath deps.txt搜索kotlin-stdlib和kotlin-gradle-plugin的版本号看是否存在多个版本共存。混合工程里这个问题比纯 Android 工程常见得多。5. 构建日志里的三个高频报错怎么拆5.1 “tag number over 30 is not supported”的定位过程这个报错是热搜词里的高频问题我在升级后第二天就在 CI 日志里看到了它。先说结论在我的项目里最终定位到的是一个两年多没更新的第三方统计 SDK它携带的字节码比较老在旧版 R8 兼容模式下能混过去切到 AGP 9.0 默认的 R8 全量模式后D8 在 dex 转化阶段处理它的字节码时触发了内部限制。排查链路是这样的第一步先确认报错出现的时机。这个报错基本只在 Release 构建且开启代码压缩时出现Debug 构建不会走到 R8 的流程。如果 Debug 构建正常、仅 Release 出问题直接怀疑 R8。第二步做一次关闭混淆的对照构建。如果关闭后构建通过问题锁定在 R8 阶段而不是清单、资源或其他环节。./gradlew :app:assembleRelease -Pandroid.enableR8.fullModefalse --stacktrace第三步用--stacktrace和--info拿到完整堆栈定位到具体是哪个 Task、哪个依赖在报错。这一步能帮助判断是主工程代码还是第三方库引入的。第四步排除法。把第三方依赖逐个移除或替换二分定位。对大型工程来说这一步最费时间但最有效。定位到问题 SDK 后处理方式通常有两种升级到跟 AGP 9.0 兼容的新版本在proguard-rules.pro里针对该 SDK 补充 keep 规则。如果 SDK 已经没人维护那就得考虑换方案了。tag number over 30这个报错本身不是 AGP 9.0 的 bug更像是新工具链对旧字节码的严格校验。你会遇到说明项目里大概率存在某个长期没更新的依赖处理它的过程其实是清理项目债务的过程。5.2 依赖冲突与 Transform API 失效升级 AGP 9.0 后另一个比较常见的报错来自依赖配置的旧写法。第三方库的文档如果还停留在五六年前很可能会教你在dependencies里用compile、provided、apk这些配置。这些写法在 Gradle 9.0 里已经被移除碰到这种报错构建日志会直接提示找不到对应方法需要把依赖配置改成implementation或compileOnly。Transform API 是另一个升级“重灾区”。AGP 9.0 对 Transform API 的态度是彻底移除如果项目引入了基于旧 Transform API 的三方插件比如某些埋点、热修、字节码插桩工具升级后会出现两种后果要么构建直接报错要么插件静默失效。静默失效比报错更可怕因为构建是绿的但产物里没有插桩逻辑问题到线上才暴露。排查时先看build.gradle里所有classpath声明的第三方插件逐个确认它们对 AGP 9.0 的兼容情况。找插件作者的新版本或替代方案如果插件已经废弃需要自己用 ASM 写一个 Gradle Task 实现字节码插桩工作量大但这是长期可持续的方案。这个取舍建议在升级前就评估清楚不要等 CI 红了再临时抱佛脚。5.3 二分定位的通用思路升级类问题最忌讳一次性改太多变量。我见过有人把 Gradle、AGP、Kotlin、JDK 四个版本同一时间全升了出问题后根本不知道是哪个环节引入的。正确的做法是控制变量一次只升一个组件每次升级都独立提交、独立验证。如果报错实在定位不了还有一个通用手段用git bisect配合构建脚本的提交历史找回归点。把构建脚本的每次修改当成一次普通代码提交在“升级前正常”和“升级后异常”之间做二分脚本能自动帮你定位到具体是哪一次改动引入的问题。另外一个很实用的排查技巧是加--offline参数跑一次构建。有些报错不是代码或配置问题而是构建过程中某个依赖下载失败引发的连锁反应。用了--offline后Gradle 会直接用本地缓存虽然可能因为缺依赖报错但能把“网络/仓库问题”和“构建逻辑问题”区分开排查方向会更清晰。6. 效率验证与进一步加速配置6.1 开启 Configuration Cache 的正确姿势Configuration Cache 的价值前面已经说过这里聊怎么安全开启。Gradle 9.0 下启用配置缓存最简单的方式是每次构建加参数./gradlew :app:assembleDebug --configuration-cache如果想常态化开启在gradle.properties里配置org.gradle.configuration-cachetrue org.gradle.configuration-cache.problemswarnproblemswarn是我推荐新手期使用的策略。第一次开启配置缓存时构建脚本里如果有不兼容的操作比如在配置阶段直接访问文件系统、执行外部命令、依赖环境变量Gradle 会报兼容性问题。把problems设为warnGradle 会先给你警告而不是直接失败让你有机会逐步修复。等所有警告都清干净了再改成problemsfail让后续任何不兼容的写法都直接暴露出来。配置缓存不是开一键就能完美运行的它逼着构建脚本走向更规范、更声明式这个改造过程本质上也是对项目构建质量的提升。6.2 gradle.properties 里值得调整的参数构建效率提升除了升级版本参数调整同样重要。以下是我在升级后实际使用的配置org.gradle.jvmargs-Xmx6g -XX:MaxMetaspaceSize2g -Dfile.encodingUTF-8 org.gradle.paralleltrue org.gradle.cachingtrue org.gradle.workers.max8 android.useAndroidXtrue android.nonTransitiveRClasstrueorg.gradle.jvmargs里的堆内存不要盲目给大。开发机 16G 内存的情况下-Xmx6g是个比较平衡的值给到 12g 反而会导致其他应用内存不足系统整体变卡。MaxMetaspaceSize2g主要防止大型项目类元数据过多导致的 OOM。org.gradle.workers.max8控制的是并行执行任务的最大 worker 数不是越大越好。这个值通常和 CPU 逻辑核数保持一致超过核数反而会增加线程切换开销。8 核 CPU 的机器设 8 是比较合适的值。android.nonTransitiveRClasstrue这个参数可能被很多人忽略。它会让每个模块只依赖自己真正引用的资源 R 类不再传递整个依赖链的 R 类编译时间和产物体积都能受益。这个参数早在 AGP 4.1 就引入了但很多老项目没有开启升级到 AGP 9.0 时建议顺手加上。6.3 用 profile 和构建扫描量化收益效率提升要用数据说话。除了前面提到的--profile本地报告我还习惯用构建扫描来做更细粒度的分析。执行构建时加--scan构建结束时会生成一个网页报告里面有每个任务的耗时、缓存命中情况、配置阶段和执行阶段的时间占比。不过构建扫描上传依赖外部网络环境如果团队网络受限本地--profile报告已经完全够用。对比时要特别注意缓存的影响。升级后的第一次构建因为 Gradle 版本和 AGP 版本都变了本地构建缓存、配置缓存基本都是空的耗时必然偏高不能拿这次数据代表真实水平。正确做法是升级后连续构建三次取后两次的耗时做对比同时确认org.gradle.cachingtrue正常工作。实测下来同一个 56 模块的项目升级前 clean build 约 262 秒升级后稳定在 178 秒左右配置阶段从 18 秒降到 2 秒总体接近 32% 的提升。如果项目规模更大、依赖更长、构建基线本身就很慢50% 是有可能的。关键在于优化组合能做到什么程度——配置缓存开了没有、并行开没开、R 类收缩开没开、R8 全量模式有没有利用上。把这几项都做到位效率提升是叠加的。最后说一个我自己的习惯升级 AGP 这种事我从来不在周五下午动手。原因不是玄学而是升级一定会牵连出依赖、插件、CI 脚本里的隐藏问题这些问题往往要几个来回才能清完。周末前留给自己的兜底时间越少越容易在赶时间时做出“先关掉配置缓存再说”这种决定。把构建时间基线留在升级前把每一步单独提交出问题时能定位到具体那一次改动这比任何优化参数都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价