项目的 Java 版本该怎么设置?

作为一个 Android 开发,走出第一步往往是很困难的,因为要面临巨复杂的环境配置问题,一个是 gradle 与 AGP 之间纠缠不清的关系,另一个就是 java 版本相关的各种配置。今天我们就回归基础,一起梳理下 java 版本相关的各种配置是为了什么。

基础知识

我们知道,Java/Kotlin 语言需要由源代码(.java / .kt 文件)编译成字节码存放在 .class 文件中。Android 还会打包成 dex 文件,最终运行在虚拟机上。

Java 语言在不断发展,每个新版本都会引入新的语言特性和语法,以便开发者编写功能更强大的程序。这些新增的语言特性都只存在于源代码中,被称为 Java 语言源代码特性。例如,Java 8 增加了 lambda 表达式。

class 文件包含了 Java 程序运行时的二进制信息,如数据类型、方法信息等。class 文件格式也是在不断演化的,其被称之为 java 二进制特性,与 java 语言源代码特性不同,Java 二进制特性的演进相对比较保守,通常多个 Java 版本会保持相同的 class 文件格式。只有在重大语言特性修改时,才会引入新的class文件格式。所以 Java 语言版本和 class 文件格式版本不完全对应。

我们配置时,就需要同时考虑 java 言源代码特性版本的配置和 java 二进制特性版本的配置

java 二进制特性版本的配置

与服务端不同的是,我们的应用是跑在用户的手机上,用户可能用了低版本的 Android ,那么其 jvm 环境也就是低版本了。所以我们需要将我们的代码编译成低版本 jvm 环境也能运行的字节码。

如果是 java, 那么可以通过 targetCompatibility 来配置:

kts 复制代码
compileOptions {
    targetCompatibility = JavaVersion.VERSION_11
} 

如果是 kotlin,则通过 jvmTarget 来配置

kts 复制代码
kotlinOptions {
    jvmTarget = "11"
}

现在我们设置目标为 java 11, 那么如果我们用户的手机系统低于 Android 10, 不支持 java 11,为何也能够正常工作呢?

这就要提到 desugaring 技术(google/desugar_jdk_libs)了,它可以将高版本的语法糖转换为低版本的实现,其实现是从字节码层面做转换。

那为什么我们不将目标版本设置得更低,而是要用 desugaring 技术呢?

因为这个 desugaring 技术可以通过 if/else 之类的手段判断 Android 版本,低版本才走 desugaring 转换后的实现,高版本保持使用高级特性,从而享受高级特性带来的性能提升。

那为什么要设置为 java 11 呢?

那是因为目前 desugar_jdk_libs 就只支持到了 java 11,所以目前主流的 Android 项目目前最高也只能设置到 java 11。

Java 语言源代码特性版本的配置

我们回到 Android 的编译上,Android 使用 gradle 进行编译,而 gradle 也是跑在 java 环境的,它也是要求 java 环境的,例如最新的 8.x 系列是跑在 jdk 17 上,我们可以在 Android Studio > Setting > Build Tool > gradle 中配置。

由于 java 新版本总会出各种新的特性,所以我们不能用 jdk 17 去编译出 java 11 的字节码,即使编译得通,也容易出翔。在 gradle 旧版本,gradle 所运行的 jdk 环境与编译源码的 jdk 耦合比较严重,所以配置会出现相互影响与相互约束的情况。

在 gradle 7.4 出了 javaToolChain 的东西,它可以让我们配置编译源代码采用单独的 jdk。

kts 复制代码
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(11))
    }
}

如果使用了 kotlin,则配置:

kts 复制代码
kotlin {
    jvmToolchain(11)
}

当然,kotlin 要求版本至少为 1.5.30。

Android 还可以设置 sourceCompatibility,其指明源码使用的 java 版本,有了 toolchain 后,它主要给 Android Studio 用,用于 lint 提示我们不要写出高版本的语法糖。

其实我们可以发现,toolchain 出来后,就可以把我们从 gradle 运行、java 源代码编译、kotlin 源代码编译采用 jdk 的严重耦合中解放出来,配置更灵活了,当然配置项也更多了,也有一定的理解成本,大家也需要明白各自的用途才能玩得很顺。

最后

gradle 虽然还是很慢,但是也不断有很多很好的改进点,早升级早享受。目前我们还可以将所有的配置项抽取出来,使用 Composing builds 管理,具体可以参考官方的 Now in Android 项目或者我的 emo 项目的 build-logic。


我是古哥E下,前微信读书客户端程序猿 / 自学 5 年中医,维护过上万 Star 开源项目 QMUI Android,现独立维护好用简洁的 Android 组件库 emo。

关注我可得:ChatGPT 开发玩法 | 程序员学习经验 | 组件库新变动 | 中医健康调理 。

emo官网:emo.qhplus.cn

相关推荐
Frankie_stand2 小时前
测试自动化实战指南:从脚本化、自动判据到 CI 集成的完整落地路径
android·ci/cd·自动化
恋猫de小郭3 小时前
Flutter 大小核优化又被回滚,然后又开新提案?
android·前端·flutter
Kapaseker3 小时前
领导问我 Kotlin 的 Nothing 是什么?
android·kotlin
韩曙亮3 小时前
【错误记录】安卓 adb 调试 5037 端口占用排查 ( adb 的三层结构 | 找出本机占用 5037 的进程 )
android·数据库·adb·安卓·错误记录
三少爷的鞋5 小时前
不改变异步本质,只改变代码组织:Telegram 项目的 Kotlin 协程实践
android
好的佩奇13 小时前
蓝牙之广播与扫描
android·flutter·蓝牙
传奇开心果编程15 小时前
【Jetpack Compose娓娓道来】第36课:焦点管理与键盘输入——让遥控器、键盘和输入法都“找得到北”
android·kotlin·移动开发·交互·android jetpack·ui框架·声明式ui
hai_android16 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
红尘伴蝶舞16 小时前
从秒开优化到连环崩溃:深入剖析 ViewPager2 在常驻 DialogFragment 中的复用陷阱与自愈方案
android·kotlin
巫山老妖21 小时前
Android 应用架构演进:MVC / MVP / MVVM / MVI (2026)
android·android jetpack