news 2026/6/12 15:56:51

Kotlin 2.4.0 正式稳定!Android 升级、Compose、KMP 全变化详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kotlin 2.4.0 正式稳定!Android 升级、Compose、KMP 全变化详解

前几天,JetBrains 发布Kotlin 2.4.0稳定版。

这次更新里针对Android开发者主要有:语言特性转正、标准库补 API、Gradle/AGP 版本边界、Compose compiler 的增量编译变化。除此之外,KMP、Wasm、JS 也有不少内容。

升级入口

Gradle 项目里,版本号还是从 Kotlin 插件开始改。如果项目已经使用 Compose compiler Gradle plugin,也要一起对齐到同一个 Kotlin 版本。

plugins{id("org.jetbrains.kotlin.android")version"2.4.0"id("org.jetbrains.kotlin.plugin.compose")version"2.4.0"}

Kotlin 2.4.0 声明兼容Gradle 7.6.3Gradle 9.5.0。Android 工程还要看一条更直接的限制:最低支持的 Android Gradle Plugin 版本提升到8.5.2。也就是说,旧项目如果还停在 AGP 8.4 或更早,不能只改 Kotlin 版本。

plugins{id("com.android.application")version"8.5.2"id("org.jetbrains.kotlin.android")version"2.4.0"}

还有一个容易漏的点:-language-version=1.9从 2.4.0 开始不再支持,K1 编译器也不再支持。仓库里如果显式写过languageVersion.set(KotlinVersion.KOTLIN_1_9),或者公共 Gradle convention plugin 里还保留旧配置,升级时会先卡在编译配置上。

稳定特性

2.4.0 把几项之前还在实验阶段的能力转成稳定状态。语言侧包括context parameters@allproperty meta-target、注解 use-site target 的新默认规则,以及 explicit backing fields。标准库侧包括 common 里的kotlin.uuid.Uuid、排序检查 API、JVM 上无符号整数转BigInteger

context parameters现在不需要 opt-in,但context arguments和 callable references 还不在稳定范围内。这个边界要分清楚,否则很容易以为context相关写法全都稳定了。

class Logger{fun info(message: String)=println(message)}context(logger: Logger)fun trackScreen(screen: String){logger.info("open$screen")}

显式context实参仍然是实验特性,用来处理只靠 context 参数区分重载时的二义性。要试这个写法,需要单独打开编译参数。

kotlin{compilerOptions{freeCompilerArgs.add("-Xexplicit-context-arguments")}}

这种能力在普通 Android 页面里不一定马上用上,但在一些 DSL、日志、权限上下文、Repository scope 里会更容易出现。稳定的是基础语法,不代表团队要立刻把现有代码改成 context 风格。

集合字面量

集合字面量是 2.4.0 里比较容易被注意到的新实验特性。打开-Xcollection-literals后,可以用[]创建集合。

kotlin{compilerOptions{freeCompilerArgs.add("-Xcollection-literals")}}

最小写法如下:

val names: MutableList<String>=["Tom","Jerry","Spike"]val tags=["android","kotlin","compose"]// 没有足够类型信息时,默认推断为 List

这里不是简单把listOf()换个符号。编译器会根据期望类型选择具体集合类型,也支持通过operator fun of接到自定义类型上。比如矩阵、路由表、测试参数这类 DSL,后面会有更短的写法。

class Matrix(vararg val rows: Row){companion object{operator fun of(vararg rows: Row)=Matrix(*rows)}class Row(vararg val values: Double){companion object{operator fun of(vararg values: Double)=Row(*values)}}}val identity: Matrix=[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0],]

限制也要记一下:当前集合字面量不能用来构造 Java 里定义的集合类型。Android 项目里如果只是写业务列表,listOf()仍然更稳;如果是库作者或 DSL 作者,可以单独开实验模块验证。

标准库补齐

kotlin.uuid.Uuid进入 common 标准库稳定状态。KMP 项目里,以前如果只是为了 UUID 引入额外库,现在可以先看标准库能不能覆盖。需要注意的是,V4/V7 生成函数还在实验阶段,仍然需要 opt-in。

排序检查 API 也转正了。以前判断列表是否有序,常见写法是自己写循环,或者先sorted()再比较。2.4.0 里可以直接用isSorted()isSortedBy()这一组 API。

data class User(val name: String, val age: Int)val numbers=listOf(1,2,3,4)println(numbers.isSorted())//truevalusers=listOf(User("Alice",24), User("Bob",31), User("Charlie",29),)println(users.isSortedBy(User::age))//false

这组 API 遇到第一对逆序元素就会停止,不需要真的构造一个排序后的集合。比如本地缓存、分页数据、服务端返回列表做断言时,用它比items == items.sortedBy { ... }更直接。

Map 也补了 nullable value 的 fallback API,不过还是实验状态。它解决的是一个老问题:key 不存在和 key 存在但 value 是null,以前用getOrPut()很容易混在一起。

@OptIn(ExperimentalStdlibApi::class)funmain(){val mapForNull=mutableMapOf<String, String?>("user"to null)val mapForMissing=mutableMapOf<String, String?>("user"to null)mapForNull.getOrPutIfNull("user"){"default_user"}mapForMissing.getOrPutIfMissing("user"){"default_user"}println(mapForNull)//{user=default_user}println(mapForMissing)//{user=null}}

这类 API 在缓存里更有用。比如接口查询结果允许为空,空结果本身也要缓存,那就应该区分“没有查过”和“查过但结果为空”。

JVM 和注解

Kotlin/JVM 现在可以生成 Java 26 字节码。Android App 日常不会直接用到 Java 26 target,更多是服务端、桌面或工具链项目会先碰到。

和 Android 生态更相关的是 metadata 里的注解默认开启。编译器会把注解写进 Kotlin metadata,元数据工具、编译期处理器、静态分析工具可以从 metadata 层读取注解信息,不必总是绕到反射或源码解析。

这类变化一般不会要求业务代码改一行,但会影响框架和工具的实现方式。比如做 KSP、API 检查、二进制兼容检查、文档生成时,metadata 里拿到的信息会更完整。

Compose compiler

Kotlin 2.4.0 里的 Compose compiler 有一条 Android 开发者需要看:internal 类型的稳定性推断在增量编译中更一致。

以前跨文件使用internalclass 作为@Composable参数时,某些稳定性变化可能不会触发相关使用点重新编译。2.4.0 改成运行时推断这类 internal 类型的稳定性,这样增量编译下的结果更一致。

代价是产物体积可能增加:当@Composable函数参数里用了不同文件的internalclass,编译器可能同时编码 stable 和 unstable 两条执行路径。完整应用优化时,R8 能把不需要的路径消掉。

// ui/state/UserUiState.kt internal data class UserUiState(val name: String, val premium: Boolean,)// ui/ProfileCard.kt @Composable internal fun ProfileCard(state: UserUiState){Text(text=state.name)}

这种代码本身不需要改。升级后更应该看的是 release 包体积、R8 是否正常开启,以及 Compose 编译报告里稳定性相关的变化。若项目里还配置了旧 feature flag,也要清掉:StrongSkippingIntrinsicRemember已经进入DeprecationLevel.ERROR,后面会移除;OptimizeNonSkippingGroupsPausableComposition也进入弃用流程。

KMP 变化

Kotlin/Native 这次变化不少。CMS GC 从 2.4.0 开始默认启用,标记阶段可以和应用线程并发执行。KMP 做 iOS UI 或共享业务模块时,这类变化主要影响暂停时间和响应性。如果遇到回归,可以在gradle.properties里退回旧策略。

kotlin.native.binary.gc=pmcs

Swift export 进入 Alpha,并且补了结构化并发和Flow导出到 Swift。suspend函数可以映射成 Swift 的async调用,Flow<T>可以导出成AsyncSequence

fun flowOfNames(): Flow<String>=flowOf("Alice","Bob")
fortry await nameinflowOfNames().asAsyncSequence(){print(name)}

KMP iOS 侧还有一个版本边界:Apple target 默认最低版本上调,iOS/tvOS 从 14.0 到 15.0,macOS 从 11.0 到 12.0,watchOS 从 7.0 到 8.0。业务如果还要覆盖更低系统,需要显式覆盖 konan properties。

kotlin{targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach{binaries.configureEach{freeCompilerArgs+="-Xoverride-konan-properties=minVersion.ios=14.0"}}}

另外,Gradle 里可以声明 Swift Package 依赖了。项目原来用 CocoaPods 管 iOS 依赖的,可以单独开分支评估迁移成本。

Wasm 和 JS

Wasm 侧,增量编译稳定并默认开启。遇到问题可以在gradle.propertieslocal.properties里关闭。

kotlin.incremental.wasm=false

WebAssembly Component Model 进入实验支持。这个方向更偏浏览器外的 Wasm,比如 wasi:http、serverless 场景。Android 项目短期内不会直接受影响,但做 KMP Web 或工具链的人可以跟一下。

JS 侧,@JsExport可以导出 inline value class 到 TypeScript,js()内联代码支持 ES2015 语法,包括constlet、class、generator、箭头函数、展开运算符、模板字符串等。

@JsExport @JvmInline value class Email(val address: String){init{require(address.contains("@")){"Invalid email"}}}

对写纯 Android App 的团队,这部分不一定要投入时间;对做 KMP SDK、Web 前端互操作、TypeScript 声明导出的团队,2.4.0 的 JS 变化更实用。

最后

都AI时代了,还有人关注Kotlin的新特性吗?

[#Kotlin](javascript:😉 [#Android开发](javascript:😉 [#JetpackCompose](javascript:😉 [#KMP](javascript:😉 [#Gradle](javascript:😉

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 2:16:28

040、StructuredOutput 结构化输出:让子代理返回 JSON Schema 验证的数据

040、StructuredOutput 结构化输出:让子代理返回 JSON Schema 验证的数据 从一次凌晨的告警说起 凌晨两点,PagerDuty 把我从床上拽起来。看了一眼告警内容:生产环境某个子代理返回的数据格式异常,导致下游的聚合服务直接崩溃。我打开日志,发现那个子代理本该返回一个包含…

作者头像 李华
网站建设 2026/6/11 6:53:26

MC68HC908MR24 ADC数据寄存器与时钟配置实战解析

1. 项目概述与ADC核心价值在嵌入式系统开发&#xff0c;尤其是工业控制、电机驱动和传感器信号采集这类对实时性和精度有双重要求的场景里&#xff0c;模数转换器&#xff08;ADC&#xff09;扮演着连接物理世界与数字处理核心的桥梁角色。我接触过不少基于8位MCU的项目&#x…

作者头像 李华
网站建设 2026/6/11 7:30:52

3分钟掌握Borderless Gaming:告别游戏窗口边框的终极解决方案

3分钟掌握Borderless Gaming&#xff1a;告别游戏窗口边框的终极解决方案 【免费下载链接】Borderless-Gaming Play your favorite games in a borderless window; no more time consuming alt-tabs. 项目地址: https://gitcode.com/gh_mirrors/bo/Borderless-Gaming B…

作者头像 李华
网站建设 2026/6/11 5:33:05

专业数据可视化工具实战指南:3步创建交互式图表

专业数据可视化工具实战指南&#xff1a;3步创建交互式图表 【免费下载链接】charticulator Interactive Layout-Aware Construction of Bespoke Charts 项目地址: https://gitcode.com/gh_mirrors/ch/charticulator Charticulator是一款革命性的开源数据可视化工具&…

作者头像 李华
网站建设 2026/6/9 19:02:17

嵌入式硬件设计:Kinetis K53引脚复用与LQFP/MAPBGA封装对比实战

1. 项目概述&#xff1a;从引脚复用说起 在嵌入式硬件设计的日常里&#xff0c;最让人又爱又恨的莫过于芯片的引脚。爱的是&#xff0c;它们是连接微控制器与外部世界的唯一桥梁&#xff1b;恨的是&#xff0c;当你的项目功能越来越复杂&#xff0c;需要的UART、SPI、I2C、ADC、…

作者头像 李华
网站建设 2026/6/11 23:41:23

食堂阿姨看了都要点赞:这个“栈“究竟是个什么鬼?

先别急&#xff0c;我们来看看以下场景想象一下&#xff0c;你在一个特别小的食堂里打饭。食堂阿姨贼凶&#xff0c;只从一头上菜。你端着餐盘走过去&#xff0c;阿姨依次给你放了&#xff1a;米饭 → 红烧肉 → 青菜 → 煎蛋好家伙&#xff0c;堆了四层。但你端到座位上准备吃…

作者头像 李华