Android 构建报 Inconsistent JVM-target:对齐 Java、Kotlin 与 buildSrc
Java 与 Kotlin 编译目标不一致时,按任务路径定位 app、buildSrc 或构建约定,区分运行 JDK、工具链和产物目标,保留项目原有兼容要求。
构建出现 Inconsistent JVM-target compatibility detected,并列出 compileDebugJavaWithJavac 与 compileDebugKotlin 的不同版本,例如 Java 为 1.8、Kotlin 为 17。它检查的是相关编译任务的 JVM 目标。把 Android Studio 的 Gradle JDK 换成 17,未必会改掉已经写死的 1.8。
还有一种常见情况:错误实际来自 :buildSrc:compileKotlin。此时反复改 app 的 compileOptions,不会直接修好 buildSrc 自己的 Java/Kotlin 编译任务。
本文面向使用 Kotlin Gradle Plugin 的 Android 工程及其 JVM 构建逻辑。示例采用普通 Android 模块 DSL;AGP 9 内置 Kotlin、Kotlin Multiplatform 等项目应按各自插件契约配置,不直接复制插件声明。
从任务路径确认改哪一层
先保留完整错误,包括失败任务、两项任务名称以及括号里的目标值。例如:
Execution failed for task ':buildSrc:compileKotlin'.
Inconsistent JVM-target compatibility detected for tasks
'compileJava' (21) and 'compileKotlin' (1.8).
这里的 21 是 Java 编译目标,不能单靠它断言当前 Gradle 一定运行在 JDK 21 上。显式配置、默认值、工具链及约定插件都可能产生这些目标。
在项目根目录查看 Wrapper 的实际 Gradle/JVM 信息,Unix 类环境示例:
./gradlew --version
Windows 使用 gradlew.bat --version。同时记录 AGP、Kotlin 插件版本以及失败模块的配置。IDE 按钮、普通终端和 CI 可能通过不同入口选择运行 JDK;新项目还可能使用 Gradle Daemon JVM criteria。不要仅看终端的 java -version 就把它当作所有入口的构建 JVM。
然后搜索该模块、构建约定插件和任务配置中的 jvmTarget、targetCompatibility、sourceCompatibility、jvmToolchain。同一值可能先在公共约定中设置,又被任务级配置覆盖。要修改最终作用于报错任务的配置。
三种版本承担不同职责
运行 Gradle 的 JDK 负责执行构建工具和插件,必须满足 Wrapper 与 AGP 的要求。例如 AGP 8.0 要求 JDK 17,但这不表示所有使用 AGP 8.0 的应用都必须把字节码目标升到 17。
Java toolchain 选择编译、测试等任务使用的 JDK。sourceCompatibility 控制 Java 源码可用的语言级别,targetCompatibility 控制 Java 编译产物目标,Kotlin 的 jvmTarget 控制 Kotlin 产物目标。相关 Java/Kotlin 任务需要相容的目标配置,运行 JDK 和这些目标可以不同。
目标级别也不等同于 Android API 可用范围。把目标改成 17 不会让旧系统自动拥有所有 Java 17 类库 API;仍需结合 compileSdk、minSdk 和项目采用的 desugaring 检查调用。
因此,先确定项目要生成哪个级别的产物,再对齐任务。保留既定 Java 8 目标的项目可以继续对齐到 1.8;已经采用 Java 17 语言和依赖要求的项目则不能为了消除报错随意降到 1.8。
Android 模块:明确 Java 和 Kotlin 的目标
假设项目已决定使用 17,且使用支持 typed compilerOptions 的 Kotlin Gradle Plugin 2.x,下面是模块 build.gradle.kts 的配置片段。保留项目已有插件、SDK 和依赖声明:
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
这里同时写出两个目标,便于排除默认值差异。若项目约定目标是 Java 8,Java 两项使用 JavaVersion.VERSION_1_8,Kotlin 使用 JvmTarget.JVM_1_8,不要只改其中一边。
旧版 Kotlin 1.9 工程仍可能使用下面的旧 DSL,应在同一个 Android 模块中与 Java 目标配套设置:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
kotlinOptions 从 Kotlin 2.0 起已弃用,迁移时改用 typed compilerOptions,不要在新旧位置分别留下互相矛盾的值。插件版本由现有兼容组合决定,这两段是二选一的配置方式,不是要求为修一个错误顺便升级全部插件。
buildSrc 失败:在构建逻辑自己的脚本中修
buildSrc 编译的是给 Gradle 使用的构建逻辑,配置位置通常为 buildSrc/build.gradle.kts。对于已经应用 Kotlin JVM 或 kotlin-dsl 的 buildSrc,可以在该文件中选择工具链,例如:
// buildSrc/build.gradle.kts,保留原有 plugins 和 dependencies
kotlin {
jvmToolchain(17)
}
17 是示例选择,需同时符合运行该构建逻辑的 Gradle JVM及插件要求。工具链可为 Java/Kotlin 编译提供一致的默认目标,但如果脚本或约定插件还显式设置了其他目标,应一并修正,不能期待默认值覆盖显式冲突。
build-logic 等 included build 也是独立构建范围。错误路径指向哪里,就检查那里的脚本和约定;应用的 Android SDK 或 minSdk 不会决定 Gradle 插件构建逻辑该用哪个 JVM 目标。
Android 模块使用工具链还存在历史版本差异:AGP 从 7.4 支持相关工具链能力,但在 8.1.0-alpha09 之前不会完整地把工具链 JDK 同步为 Java targetCompatibility。这些版本应在 compileOptions 明确设置目标。工程也要预先安装所需 JDK,或配置适配 Wrapper 的工具链解析器;找不到工具链是另一种错误。
不关闭校验,回到同一构建入口验证
把 kotlin.jvm.target.validation.mode 改成 ignore 或 warning,只会改变是否阻止构建,不会对齐产物。Gradle 8.0 及以上项目中,这项校验默认按 error 处理;升级后才开始失败,可能只是以前的差异现在被显式拦住。
修改前保留模块脚本、构建约定和 JVM 选择配置。用原来失败的入口重建相同变体,必要时检查编译日志中的实际参数,确认报错的那一对任务都使用约定目标,再跑现有测试与设备验证。若只有 IDE 失败,继续比较 IDE 与终端的运行 JDK及生效配置。
如果新错误变成 Unsupported class file major version,要另查 Gradle、插件或字节码读取组件的运行兼容性,不能继续来回调 jvmTarget。回退时恢复本次配置差异及配套工具链选择,不删除整个缓存来掩盖版本关系。


