Kotlin Multiplatform 入门:一套 Kotlin,三端共享

KMP 解决什么问题:先划清”共享什么”

一个移动应用的代码里,UI 通常只占三四成,剩下的大头是数据模型、网络请求、业务规则、缓存与持久化——这部分在 Android 和 iOS 上往往被两拨人用两种语言写两遍,还总写出行为不一致。Kotlin Multiplatform(KMP)就是冲着这块来的:业务逻辑用 Kotlin 写一份,编译成各平台的产物,UI 默认各端用各端的原生方案。它和 Flutter、React Native 的出发点不同:那两家是连 UI 一起跨端,KMP 的第一卖点是把”逻辑写两遍”这个成本砍掉,同时保留原生 UI 的可能性。

本站之前的 Kotlin 系列都是 Android 单端视角:环境搭建、第一个 Compose 应用、协程与 Flow 基础。好消息是这些积累全部可以搬进 KMP——协程、Flow、序列化这些 Kotlin 功力在共享代码里原样可用。本文补上”多端”这一块:KMP 的定位、两条技术路线、工程结构和 expect/actual 机制。

两条路线:纯 KMP 与 Compose Multiplatform

社区里说”用 Kotlin 跨端”其实指两条不同的路线,先把它们分开,后面的决策才不混乱:

维度 纯 KMP(共享逻辑) Compose Multiplatform(逻辑 + UI 全共享)
共享范围 数据模型、网络、业务规则、存储 上述全部,再加 UI 层
各端 UI Android 用 Jetpack Compose,iOS 用 SwiftUI,桌面用 Swing/Compose 全端用 Compose 绘制,不走系统原生控件
iOS 成熟度 KMP 本体 2023-11 起稳定(Kotlin 1.9.20 宣布) 2025-05 的 Compose Multiplatform 1.8.0 起 iOS 端稳定,可上架 App Store
团队要求 两端各要有原生 UI 能力,Kotlin 核心团队维护公共层 主要是 Kotlin/Compose 技能栈,iOS 侧只需发布与原生桥接能力
适合场景 团队已有原生两拨人,痛点是逻辑重复与不一致 中小团队想一人马覆盖三端,UI 一致性优先于原生手感
主要代价 UI 仍然写两遍 放弃系统原生控件的免费更新(如新系统组件自动获得),复杂列表性能要自己调

这两条路线不是二选一的宗教站队,可以渐进:先用纯 KMP 把逻辑层统下来,验证收益后,再逐屏把 UI 挪到 Compose Multiplatform。反过来一上来全共享 UI、逻辑却没理清的项目,通常在第一个性能坑就退回去了。

版本与稳定性现状

跨端技术最怕”学的时候还稳定,上线时已翻篇”,所以先把时间线摆出来(2025-06 核实):

时间 事件 意味着什么
2023-11 Kotlin 1.9.20,JetBrains 宣布 Kotlin Multiplatform 稳定 语言层与工具链承诺向后兼容,可以进生产
2024-05 Kotlin 2.0.0,K2 编译器默认启用并稳定 新编译器前端覆盖 JVM/Native/Wasm/JS 全目标,编译速度与智能转换显著改善
2025-05 Compose Multiplatform 1.8.0,iOS 端转稳定 Compose 写的 iOS UI 可正式上架,核心 API 有兼容承诺
当前 Kotlin 2.2 为稳定线(2025-06 GA),2.1.x 仍广泛在用 新项目直接用当前稳定线即可,具体小版本以官方发布页为准

一个常被问的问题:”iOS 端到底能不能用于生产?”以 2025 年年中的口径回答:能。KMP 逻辑层稳定已一年半,Compose Multiplatform 的 iOS 目标也在 2025-05 的 1.8.0 转入稳定,App Store 上架没有政策障碍。真正的分界线不在”能不能”,而在你团队有没有能力排查 iOS 侧的工具链问题——这需要至少一台 Mac。

工程结构:source set 怎么组织

KMP 工程按”源集(source set)”组织代码,commonMain 是共享层,各平台目录放平台专属实现。一个典型的三端工程长这样:

shared/
├── build.gradle.kts
└── src/
    ├── commonMain/kotlin/     # 共享层:数据模型、仓库、网络、业务规则
    │   └── com/example/note/
    │       ├── Note.kt            # 纯 Kotlin 数据类,三端通用
    │       ├── NoteRepository.kt  # 依赖接口,不碰平台 API
    │       └── Platform.kt        # expect 声明(见下一节)
    ├── androidMain/kotlin/    # Android 实现 + Jetpack Compose UI 所在模块引用它
    ├── iosMain/kotlin/        # iOS 实现,编译为 iOS 框架供 SwiftUI 侧调用
    └── desktopMain/kotlin/    # JVM 桌面实现(Windows/macOS/Linux)

commonMain 里只能依赖两种东西:纯 Kotlin,以及明确声明支持多平台的库。生态里常用的几块拼图:网络用 Ktor client,序列化用 kotlinx.serialization,并发用 kotlinx.coroutines,本地存储用 SQLDelight 或 Room(两者均支持多目标)。把一个 Android 专属依赖塞进 commonMain,编译器会直接报错而不是等到运行时——这是 source set 机制的价值:平台泄漏在编译期被拦下,而不是上线后崩给你看。

expect/actual:跨端的契约机制

共享代码总有绕不开平台差异的时刻:读个设备标识、发个本地通知、拿个主线程。KMP 的答案是 expect/actual——commonMain 里用 expect 声明”我承诺每个平台都有这个东西”,各平台目录用 actual 提供实现:

// commonMain/Platform.kt
// expect 只写契约不写实现:像接口,但由编译器逐目标检查
expect fun currentPlatform(): String

// commonMain/NoteRepository.kt
fun greeting(): String = "running on ${currentPlatform()}"
// androidMain/Platform.android.kt
actual fun currentPlatform(): String = "Android ${android.os.Build.VERSION.RELEASE}"

// iosMain/Platform.ios.kt
import platform.UIKit.UIDevice
actual fun currentPlatform(): String = "iOS ${UIDevice.currentDevice.systemVersion}"

// desktopMain/Platform.desktop.kt
actual fun currentPlatform(): String = "Desktop ${System.getProperty("os.name")}"

它和运行时反射方案的差别值得强调一遍:某个目标忘了写 actual,编译直接失败,错误信息会点名缺哪个目标的实现;不会出现”测试环境好好的,某个平台一跑就 NoSuchMethod”的运行时惊喜。代价是新增一个目标(比如从三端扩到 wasm)时,所有 expect 都要补对应 actual——契约是按目标数量倍增的,这决定了 expect 声明必须克制。

反模式点名:expect/actual 泛滥。一个健康工程里 expect 通常只有个位数到十几处,集中在平台能力薄薄一层;如果你发现 commonMain 里几十个 expect,说明平台差异没有被抽象掉,只是被切成碎片藏了起来,每加一端都要还一遍债。正确做法是把差异封装成小的平台服务接口,业务代码依赖接口而不是依赖 expect 函数。

跑起来:创建第一个工程

官方提供在线脚手架 Kotlin Multiplatform wizard(kmp.jetbrains.com),勾选目标平台与要集成的库(Networking 选 Ktor、Storage 选 Room 都可以),下载即得可运行的 Gradle 工程;Android Studio 与 JetBrains Fleet 也内置了同源向导。生成的工程直接承载上文的 source set 结构,不需要手工拼 Gradle 配置。命令不钉版本,脚手架产出的 Kotlin 版本以生成时官方模板为准,生成后第一件事是把实际版本记进项目文档。

三条来自常见踩坑的提示:

检查项 原因
勾了 iOS 目标,构建机必须是 macOS 且装好 Xcode iOS 目标的编译与框架产物要走 Xcode 工具链,Linux/Windows 只能编非 Apple 目标
本地跑通 Android 与桌面目标再碰 iOS 前两者不需要额外环境,用最低成本验证共享层逻辑
共享层先写纯逻辑(模型 + 仓库 + 一条 Ktor 请求) 第一天就集成数据库与加密存储,排障时分不清是 KMP 的问题还是库的问题

入门阶段的另外两个坑

坑一:把”KMP 稳定”当成”我用的库都稳定”。稳定承诺作用于语言与工具链层面;具体某个多平台库的 API 成熟度要看它自己的文档(标注 Stable/Beta/Experimental)。尤其 composable UI 相关的实验 API,升级时行为变化不提前通知。给公共层选库时,把”是否标注 Stable”当硬条件。

坑二:一上来就追求”百分百共享”。共享率不是 KPI,百分之百共享通常意味着用最别扭的方式绕开平台差异。留出各端的 actual 层,让每端用它最顺手的系统 API,长期成本反而更低。

这篇教会了什么,下一步去哪

三句话收束:KMP 共享的是逻辑不是 UI,先统一逻辑层再看要不要全共享;expect/actual 是编译期契约,用得越少越健康;iOS 稳定性在 2025 年已不是 blocker,真正的门槛是团队的工具链排障能力。

下一步按顺序读:第一个 Compose 应用补 Android 侧 UI(Compose 语法在 Compose Multiplatform 里原样复用),协程与 Flow 基础补共享层里最常用的异步工具——协程与 Flow 在 commonMain 里一行不改地跨三端。

版本核实(2025-06 检索):Kotlin 当前稳定线 2.2(2025-06 GA,2.1.x 仍广泛在用);Kotlin Multiplatform 于 2023-11 随 Kotlin 1.9.20 宣布稳定;K2 编译器自 2024-05 的 Kotlin 2.0.0 起默认启用;Compose Multiplatform for iOS 自 2025-05 的 1.8.0 起稳定。以上均以 kotlinlang.org 官方发布页为准。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇