AGP 9 升级说明文档

一、升级概览

1.1 版本变更总览

组件 升级前 升级后 说明
Android Gradle Plugin (AGP) 8.6.0 9.2.1 主升级目标
Gradle 8.x 9.4.1 AGP 9 强制要求
Kotlin 2.1.0 2.3.0 AGP 9 内置 Kotlin 要求对齐
KSP 2.1.0-1.0.29 2.3.6 跟随 Kotlin 2.3(新版本号方案)
Room 2.5.2 2.8.4 修复 KSP2 兼容性 bug
compileSdk 36 36 不变
minSdk 26 26 不变
targetSdk 36 36 不变
JDK 17 17 不变(AGP 9 最低要求 17)

1.2 升级收益

  1. 构建性能:内置 Kotlin、非 final R class(本项目暂保留 final)、配置缓存优化
  2. 现代化 DSL:统一走 Variant API、移除大量 deprecated API
  3. R8 优化:默认开启更激进的代码优化与资源压缩
  4. 工具链对齐:与 Android Studio 2026.1.1(Meerkat)完美匹配

1.3 升级耗时

  • 实际编码与调试:约 2 个工作日
  • 涉及修改文件:30 个(含 2 个文件重命名)
  • 解决各类问题:12 类(环境、版本、DSL、API、编译)

二、环境前置条件

2.1 必备环境

要求
Android Studio 2025.2(Otter)或更新,推荐 2026.1.1(Meerkat)
JDK 17 或更高(AGP 9 最低要求 17)
Gradle 9.1.0+(项目实际使用 9.4.1)
SDK Build Tools 36.0.0
NDK 28.2.13676358(如用到)

2.2 网络环境(关键)

AGP 9.2.1 全套依赖(约 30+ jar)需从 Google Maven(dl.google.com)下载,国内网络环境需配置代理。本项目在 gradle.properties 中配置了项目级代理:

systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=9098
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=9098
# 内网仓库不走代理
systemProp.http.nonProxyHosts=localhost|127.0.0.1|10.*|*.sohuno.com|*.sohurdc.com
systemProp.https.nonProxyHosts=localhost|127.0.0.1|10.*|*.sohuno.com|*.sohurdc.com
# 代理网络不稳时增加超时容忍
systemProp.org.gradle.internal.http.connectionTimeout=120000
systemProp.org.gradle.internal.http.socketTimeout=120000

注意:代理配置变更后必须 kill 所有 Gradle/Kotlin daemon,否则会沿用旧环境变量。

pkill -f GradleDaemon
pkill -f KotlinCompileDaemon

2.3 代理配置清理(踩坑记录)

升级过程中遇到多次代理冲突,需要排查并清理以下三处:

位置 文件 处理方式
全局 Gradle ~/.gradle/gradle.properties 删除 systemProp.http.proxyHost 等行
Shell 环境 ~/.zshrc export ALL_PROXY=... 改为 proxy_on() / proxy_off() 函数
IDE 配置 ~/Library/Application Support/Google/AndroidStudio2026.1.1/options/proxy.settings.xml 清空 PROXY_HOST / PROXY_PORT 字段

同时建议在 Android Studio 设置中关闭 Download Gradle Sources,减少下载量(在弱网下尤其重要)。


三、升级步骤

3.1 第一步:AGP Upgrade Assistant

  1. 打开 Android Studio → Tools → AGP Upgrade Assistant
  2. 在 "Target Android Gradle Plugin Version" 下拉框手动选择 9.x(默认显示 8.13,需手动切换)
  3. 执行升级,让助手自动处理基础版本号和部分 deprecated 标志

3.2 第二步:移除 AGP 9 默认行为的 opt-out 标志

gradle.properties 中以下标志在 AGP 9 已变为默认行为,需移除(或注释):

# 以下 3 项已被 AGP 9 作为默认值启用,原 opt-out 标志需移除
# android.builtInKotlin=false       # 内置 Kotlin(已默认启用)
# android.newDsl=false              # 新 DSL(已默认启用)
# android.uniquePackageNames=false  # 唯一包名(已默认启用)

3.3 第三步:升级核心版本号

buildSrc/build.gradle.kts

// AGP 版本
implementation("com.android.tools.build:gradle:9.2.1")  // 原 8.6.0

gradle.properties

version_kotlin=2.3.0    # 原 2.1.0
version_ksp=2.3.6       # 原 2.1.0-1.0.29(注意 KSP 2.x 起版本号格式变化)

gradle_plugin/(独立 Gradle 插件项目)

// settings.gradle.kts
kotlin("jvm") version "2.3.0"   // 原 2.1.0

// plugins/build.gradle.kts
compileOnly("com.android.tools.build:gradle-api:9.2.1")  // 原 8.6.0
compileOnly("com.android:zipflinger:9.2.1")              // 原 8.3.0

gradle/wrapper/gradle-wrapper.properties

distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-9.4.1-all.zip

四、详细改动清单

4.1 内置 Kotlin(Built-in Kotlin)迁移

AGP 9 默认启用内置 Kotlin,不再需要显式 apply kotlin-android 插件。若仍显式 apply 会报错:

Cannot add extension with name 'kotlin', as there is an extension already registered with that name.

4.1.1 移除显式插件声明

以下 13 个模块的 plugins {} 块移除了 kotlin-android 相关声明:

模块 移除的插件
buildSrc/src/main/kotlin/hyCommKotlin.gradle.kts id("kotlin-android")
app/build.gradle.kts `kotlin-android`
comm_lib/build.gradle.kts id("kotlin-android")
report_module/build.gradle.kts id("kotlin-android")
push_module/build.gradle.kts id("kotlin-android")
share_module/build.gradle.kts id("kotlin-android")
privacy_check/build.gradle.kts id("kotlin-android")
ui_lib/build.gradle.kts id("kotlin-android")
photoedit_module/build.gradle.kts id("kotlin-android")
as_lib/build.gradle.kts id("org.jetbrains.kotlin.android")
local_third_source/sns_apm_module/build.gradle.kts id("org.jetbrains.kotlin.android")

4.1.2 迁移 kotlinOptionskotlin { compilerOptions {} }

内置 Kotlin 后,android.kotlinOptions {} DSL 不再可用,需迁移到顶层kotlin { compilerOptions {} } 块。

修改前:

android {
    kotlinOptions {
        jvmTarget = "17"
    }
}

修改后:

// jvmTarget 默认会跟随 android.compileOptions.targetCompatibility,无需显式声明
kotlin {
    compilerOptions {
        // 不再硬编码 apiVersion / languageVersion,让 Kotlin 默认跟随项目版本
    }
}

坑点:原代码硬编码了 KOTLIN_1_8apiVersion / languageVersion(2023 年为规避 daemon 警告加的),Kotlin 2.3 最低要求 2.0,会导致 Language version 1.8 is no longer supported 编译失败。必须删除硬编码,让 Kotlin 跟随项目默认版本。

涉及文件:

  • buildSrc/src/main/kotlin/hyCommKotlin.gradle.kts
  • app/build.gradle.kts
  • local_third_source/sns_apm_module/build.gradle.kts

4.2 新 DSL 迁移

AGP 9 移除/重命名了大量旧 DSL,以下是本项目涉及的改动:

旧 API 新 API 涉及文件
AppExtension ApplicationExtension app/build.gradle.kts(函数签名)
dexOptions {} (删除,AGP 9 内部优化) app/build.gradle.kts
packagingOptions {} packaging {} app/build.gradle.ktslocal_third_source/bitmap_monitor/build.gradle
android.kotlinOptions {} kotlin { compilerOptions {} }(顶层) 多个模块
srcDir(...) directories += (...) appui_libphotoedit_module、6 个 local_repository/*
task("xxx") {} tasks.register("xxx") {} app/build.gradle.kts
BaseExtension.ndkDirectory AndroidComponentsExtension.sdkComponents.ndkDirectory app/build.gradle.kts
jniLibs.srcDir("libs") jniLibs.directories += "libs" 6 个 local_repository/* 模块

4.2.1 proguard-android.txtproguard-android-optimize.txt

AGP 9 不再支持 proguard-android.txt(含 -dontoptimize,阻止 R8 优化):

// 修改前
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")

// 修改后
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")

4.3 Variant API 迁移

applicationVariants.all {}(eager、sync)→ 新 androidComponents.onVariants { variant -> ... }(lazy、provider-based)。

4.3.1 rename {} 块改造

修改前:

this.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def fileName = "${variant.productFlavors[0].name}-${variant.buildType.name}.apk"
        output.outputFileName = fileName
    }
}

修改后:

androidComponents {
    onVariants { variant ->
        variant.outputs.forEach { output ->
            output.outputFileName.set("${variant.flavorName}-${variant.buildType}.apk")
        }
    }
}

4.3.2 HySoFilesScan {} 块改造

同上方式改造,访问 variant.flavorName / variant.buildType

4.3.3 defaultConfig 在 onVariants 中不可访问

androidComponents.onVariants 块内无法直接访问 defaultConfig.versionName / versionCode,需通过 variant outputs 获取:

val versionName = variant.outputs
    .filterIsInstance<VariantOutputImpl>()
    .firstOrNull()?.versionName?.orNull
val versionCode = variant.outputs
    .filterIsInstance<VariantOutputImpl>()
    .firstOrNull()?.versionCode?.orNull

4.4 Room 升级(2.5.2 → 2.8.4)

4.4.1 升级原因

Room 2.5.2 在 KSP 2.x + Kotlin 2.3 下会崩溃:

last parameter of varargs method detete must be an array
    at androidx.room.writer.DaoWriter.createShortcutMethods(DaoWriter.kt:466)

detete 是 Room 2.5.2 的 typo bug,在新版本已修复)

4.4.2 版本修改

buildSrc/src/main/kotlin/Dependencies.kt

const val roomRuntime = "androidx.room:room-runtime:2.8.4"
const val roomRxjava2 = "androidx.room:room-rxjava2:2.8.4"
const val roomCompiler = "androidx.room:room-compiler:2.8.4"

4.4.3 Room 2.8 + KSP2 的 Java DAO 可空性 bug

Room 2.8 的 KSP2 处理器对 Java DAO 的响应式返回类型(Flowable / LiveData / Publisher)有可空性推导 bug —— 把 Java platform type Flowable<ProductBean> 推导成 Flowable<ProductBean?>?,但生成的 createFlowable 返回非空,导致 override 签名不匹配。

解决方案:在 ksp {} 块加 arg("room.generateKotlin", "false"),让 Room 生成 Java 代码(*_Impl.java)而非 Kotlin 代码(*_Impl.kt),绕过 Kotlin 严格可空性检查。

// app/build.gradle.kts
ksp {
    arg("ksp.test", "aaaa1111")
    arg("room.generateKotlin", "false")
}

涉及文件:

  • app/build.gradle.kts
  • report_module/build.gradle.kts(防御性配置)

4.4.4 DAO 可空返回类型调整

Room 2.8 对单实体查询返回类型要求可空(SQL 可能查不到记录)。以下 DAO 的 select() 返回类型加 ?

文件 改动
app/src/main/java/.../sticker/dao/StickerGroupDao.kt fun select(id: String): StickerGroupBeanStickerGroupBean?
app/src/main/java/.../tagline/dao/TagTabDao.kt fun select(tagId: String): TagTabBeanTagTabBean?
app/src/main/java/.../tagline/viewmodel/TagLineViewModel.kt 调用方加 ?: throw NullPointerException(...) 保持原 onError 兜底语义

4.5 enableAppCompileTimeRClass 配置(保持 R 字段为常量)

4.5.1 问题

AGP 9 引入新开关 android.enableAppCompileTimeRClass默认从 false 变为 true,让 app 模块也使用非 final R class(与 library 对齐)。这导致 R.id.xxx 不再是编译期常量,无法用在 switchcase 上:

错误: 需要常量表达式
    case R.id.bt_login:

4.5.2 解决方案

项目里有大量 Java 代码使用 switch(R.id.xxx),一次性全改 if-else 风险大。保持 R 字段为常量

gradle.properties

android.nonFinalResIds=false
android.enableAppCompileTimeRClass=false

4.5.3 长期建议

AGP 10 会移除该选项,届时必须把所有 switch(R.id.xxx) 迁移成 if-else 链:

  • Android Studio 提供自动转换:光标放在 switch 上 → Option+Enter → "Replace switch with if-else"
  • Kotlin 的 when 允许非常量 case,所以只需改 Java 文件
  • 可用 lint 的 NonConstantResourceId 检查扫描定位

4.6 sourceSets 配置:java 与 kotlin 分离

4.6.1 问题

AGP 9 内置 Kotlin 严格区分 javakotlin source set。原配置只把 hytest 加到了 java source set:

this.java {
    directories += "src/hytest/java"
}

旧版 kotlin-android 插件会自动扫 java 目录里的 .kt 文件,新插件不会。导致 hytest 下的 .kt 文件(TestFaceVerifyFragment.ktTestRxBusFragment.kt 等)不参与编译,Java 调用方报 找不到符号

4.6.2 解决方案

混合源码目录(同时有 .java.kt)必须显式加到两个 source set

(this.maybeCreate("debug") as com.android.build.api.dsl.AndroidSourceSet).run {
    this.java {
        directories += "src/hytest/java"
    }
    // 关键:AGP 9 内置 Kotlin 不再自动扫 java 目录的 .kt 文件
    this.kotlin {
        directories += "src/hytest/java"
    }
    this.res {
        directories += "src/hytest/res"
    }
}

4.7 文件名与类名一致性

4.7.1 问题

Kotlin 规范要求顶层类名必须与文件名一致。旧 Kotlin 是 warning,Kotlin 2.3 + AGP 9 内置 Kotlin 收紧,会导致该类对其它文件不可见(编译报 找不到符号)。

4.7.2 修复(用 git mv 保留历史,不改代码)

原文件名 新文件名 类名
app/src/hytest/.../bean/LiveDataEvent.kt LiveDataTestEvent.kt LiveDataTestEvent
app/src/main/.../chat/event/KickGroupReusltEvent.kt(有 typo) KickGroupResultEvent.kt KickGroupResultEvent

4.8 Kotlin 2.3 语法严格性

GroupChatMsgActivity.kt:260isEmpty 缺括号:

// 修改前(Kotlin 2.3 前容忍)
if (!atUsers.isEmpty) {

// 修改后
if (!atUsers.isEmpty()) {

atUsersSimpleArrayMapisEmpty() 是方法不是属性。Kotlin 2.3 不再把裸函数名当函数引用容忍。


4.9 模块级 compileSdk 显式声明

AGP 9 要求所有模块显式声明 compileSdk,不能再从其他地方继承。

问题模块privacy_check/build.gradle.kts(空壳 library,没用 hyCommAndroid 脚本)

android {
    namespace = "com.sohu.sohuhy.privacycheck"
    compileSdk = AndroidInfo.compileSdk   // 新增
    defaultConfig {
        minSdk = AndroidInfo.minSdk       // 新增
    }
}

其它模块都用了 hyCommAndroid 预编译脚本插件(在 buildSrc/src/main/kotlin/hyCommAndroid.gradle.kts 里统一设置了 compileSdk),无需改动。


五、验证结果

5.1 skill 要求的三步验证

验证步骤 结果 耗时
1. Gradle IDE Sync ✅ 通过 14m 5s
2. ./gradlew help ✅ 通过 10s
3. ./gradlew build --dry-run ✅ 通过 10m 58s

5.2 编译验证

验证项 结果
:app:kspFlavorsDevDebugKotlin ✅ 通过
:app:compileFlavorsDevDebugKotlin ✅ 通过
:app:compileFlavorsDevDebugJavaWithJavac ✅ 通过

建议后续完整验证:./gradlew :app:assembleFlavorsDevDebug 并在真机上跑一次冒烟测试。


六、受影响文件清单

6.1 构建配置文件(30 个)

app/build.gradle.kts
as_lib/build.gradle.kts
buildSrc/build.gradle.kts
buildSrc/src/main/kotlin/Dependencies.kt
buildSrc/src/main/kotlin/hyCommKotlin.gradle.kts
comm_lib/build.gradle.kts
gradle.properties
gradle/wrapper/gradle-wrapper.properties
gradle_plugin/gradle/wrapper/gradle-wrapper.properties
gradle_plugin/plugins/build.gradle.kts
gradle_plugin/settings.gradle.kts
local_repository/amap_sdk/build.gradle.kts
local_repository/csjpeg_sdk/build.gradle.kts
local_repository/gifflen_sdk/build.gradle.kts
local_repository/news_ad_sdk/build.gradle.kts
local_repository/speech_sdk/build.gradle.kts
local_repository/video_sdk/build.gradle.kts
local_repository/zxing_sdk/build.gradle.kts
local_third_source/bitmap_monitor/build.gradle
local_third_source/sns_apm_module/build.gradle.kts
photoedit_module/build.gradle.kts
privacy_check/build.gradle.kts
report_module/build.gradle.kts
ui_lib/build.gradle.kts

6.2 源码文件(5 个)

app/src/main/java/hy/sohu/com/app/chat/view/message/GroupChatMsgActivity.kt   (isEmpty 语法)
app/src/main/java/hy/sohu/com/app/sticker/dao/StickerGroupDao.kt              (DAO 可空)
app/src/main/java/hy/sohu/com/app/tagline/dao/TagTabDao.kt                    (DAO 可空)
app/src/main/java/hy/sohu/com/app/tagline/viewmodel/TagLineViewModel.kt       (调用方兜底)

6.3 文件重命名(2 个,无内容修改)

app/src/hytest/java/.../bean/LiveDataEvent.kt          → LiveDataTestEvent.kt
app/src/main/java/.../chat/event/KickGroupReusltEvent.kt → KickGroupResultEvent.kt

6.4 测试源集文件(1 个,可空性注解调整后回退)

app/src/hytest/java/hy/sohu/com/app/test/model/db/ProductDao.java

七、后续注意事项

7.1 已知的技术债

说明 建议处理时机
switch(R.id.xxx) 未迁移 依赖 enableAppCompileTimeRClass=false AGP 10 升级前必须完成
resourceConfigurations 使用了 deprecated API 已加 @Suppress("DEPRECATION") 后续迁移到 androidResources.localeFilters
大量 AGP 9 deprecation warnings excludeLibraryComponentsFromConstraints 可在 gradle.propertiesandroid.generateSyncIssueWhenLibraryConstraintsAreEnabled=false 静默

7.2 Room 2.8 行为变化留意

  1. 默认使用 KSP2:当前 KSP 2.3.6 已满足要求
  2. 数据库 schema 重新校验:2.5.2 → 2.8.4 可能重新生成 schema 文件。若有 room.schemaLocation 导出的 schema 用于迁移测试,建议跑一遍 ksp 后 diff 一下
  3. 更严格的 DAO 写法:2.6→2.8 对部分 DAO 写法更严格,出现新警告按提示改

7.3 日常开发注意事项

  1. 新增模块:无需再声明 kotlin-android 插件,AGP 9 自动支持 Kotlin
  2. 新增 Kotlin 源码目录:必须用 this.kotlin { directories += "xxx" },不能只加到 java
  3. 新增顶层类:文件名必须和类名完全一致
  4. DAO 单实体查询:返回类型记得加 ?(可空)

7.4 性能优化建议(可选)

以下配置可进一步提升构建性能,但需评估兼容性:

# 启用非传递 R class(默认 false,因项目内大量跨模块 R 引用,暂不开启)
# android.nonTransitiveRClass=true

# 启用配置缓存(需配合 AGP 9 优化)
# org.gradle.configuration-cache=true

# 启用并行构建
org.gradle.parallel=true
org.gradle.caching=true

八、参考资料


九、变更记录

日期 内容
2026-06-22 完成 AGP 8.6.0 → 9.2.1 主体升级,Sync 通过
2026-06-22 解决代理配置冲突(全局 Gradle / shell / IDE)
2026-06-22 完成内置 Kotlin 迁移(13 个模块移除 kotlin-android)
2026-06-22 完成 Kotlin 2.3.0 / KSP 2.3.6 版本对齐
2026-06-22 完成 Variant API 迁移(applicationVariants → onVariants)
2026-06-22 完成 ./gradlew help./gradlew build --dry-run 验证
2026-06-23 修复 Kotlin 1.8 硬编码(DaoWriter 崩溃)
2026-06-23 升级 Room 2.5.2 → 2.8.4(修复 detete typo bug)
2026-06-23 修复 Room 2.8 + KSP2 Java DAO 可空性 bug(room.generateKotlin=false)
2026-06-23 修复 proguard-android.txt 不再支持
2026-06-23 修复 enableAppCompileTimeRClass 导致 switch(R.id) 失效
2026-06-23 修复 sourceSets java/kotlin 分离问题(hytest)
2026-06-23 修复文件名与类名不一致(LiveDataTestEvent / KickGroupResultEvent)
2026-06-23 修复 Kotlin 2.3 语法严格性(isEmpty 缺括号)
2026-06-23 全量编译验证通过,输出本文档
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容