Kotlin Multiplatform¶
Kotlin Multiplatform,简称 KMP,用于在多个平台之间共享 Kotlin 代码。它的核心目标不是“一套代码写所有 UI”,而是把值得共享的业务逻辑、数据模型、网络协议、校验规则、数据库访问抽象等放到公共模块里,同时允许每个平台保留自己的原生能力和用户体验。
适合共享什么¶
通常适合共享:
- DTO 和领域模型。
- 表单校验、权限规则、价格计算等业务逻辑。
- REST、GraphQL、RPC 等 API 客户端抽象。
- 本地缓存、序列化、加密、日志等跨平台基础能力。
- ViewModel 或 presentation logic,取决于团队架构。
通常不强求共享:
- 深度平台相关 UI。
- Android/iOS 生命周期细节。
- 平台 SDK 强绑定能力。
- 需要平台团队分别优化的体验。
Java 对比:传统 Java 跨平台更多依赖 JVM。KMP 的重点不是把 JVM 带到所有平台,而是把 Kotlin 编译到 JVM、Android、iOS、JS、Native、Wasm 等不同目标。
基本项目结构¶
KMP 项目通常通过 source set 组织代码:
shared/
src/
commonMain/
commonTest/
androidMain/
iosMain/
jsMain/
commonMain:所有目标共享的生产代码。commonTest:共享测试。androidMain:Android/JVM 相关代码。iosMain:iOS 相关代码。jsMain:JavaScript 相关代码。
不是每个项目都需要所有 source set。只做 Android+iOS 共享时,通常只需要 commonMain、androidMain、iosMain。
commonMain 的边界¶
commonMain 只能使用公共 Kotlin API 和声明为 multiplatform 的库。你不能在 commonMain 里直接使用:
java.io.File- Android
Context - iOS
NSUserDefaults - 浏览器
window
这些 API 都属于具体平台。共享代码想访问平台能力时,应该通过抽象或 expect/actual。
expect/actual¶
expect/actual 用于在公共代码声明一个“平台会提供的能力”。
公共代码:
// commonMain
expect fun currentTimeMillis(): Long
JVM 实现:
// jvmMain
actual fun currentTimeMillis(): Long =
System.currentTimeMillis()
使用原则:
expect描述能力,不暴露平台细节。actual负责调用平台 API。- 不要把大量平台分支塞进 common 逻辑里。
依赖管理¶
依赖也按 source set 声明:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
}
androidMain.dependencies {
implementation("androidx.core:core-ktx:...")
}
}
}
只有支持对应目标的库才能放进对应 source set。一个 JVM-only 库不能放进 commonMain。
平台目标¶
常见目标包括:
- Android/JVM。
- iOS。
- macOS、Linux、Windows 等 Native。
- JavaScript。
- WebAssembly。
目标越多,公共 API 设计越要保守。比如文件系统、线程模型、时间 API、加密库、网络栈在不同平台差异很大。
测试策略¶
共享逻辑应尽量放在 commonTest 中测试:
src/commonTest/kotlin/PriceCalculatorTest.kt
平台行为需要平台测试:
- Android instrumented test。
- iOS 测试。
- JS 测试。
- Native 测试。
不要只测 JVM 目标就宣称多平台逻辑安全。不同平台的时间、浮点、并发、文件和编码行为可能存在细节差异。
与 Java 项目的关系¶
KMP 不等于 Java 模块化,也不等于 Maven 多模块。
Java 多模块通常仍在 JVM 世界里复用代码。KMP 的 source set 会编译到不同平台产物,例如:
- JVM bytecode。
- Android artifact。
- iOS framework。
- JavaScript bundle/module。
- Native binary/library。
如果团队主要是后端 Java/Kotlin,可能不需要 KMP;普通 JVM 多模块就足够。只有当业务确实需要跨 Android、iOS、Web、Native 等共享逻辑时,KMP 才值得引入。
常见误区¶
误区一:所有代码都应该共享¶
共享是有成本的。平台差异越大,强行共享越容易让公共层变成抽象垃圾桶。
误区二:KMP 会消灭平台工程师¶
KMP 更现实的价值是减少重复业务逻辑。平台 UI、性能调优、平台 SDK 集成仍需要平台经验。
误区三:commonMain 可以随意调用 Java 库¶
不可以。commonMain 不是 JVM。只有 JVM/Android source set 才能使用 JVM-only 库。
误区四:expect/actual 是跨平台 if-else¶
expect/actual 应该用于少量平台能力抽象。如果到处都是 expect/actual,说明公共模型可能设计得太贴近平台实现。
实践建议¶
- 先共享稳定业务逻辑,不要一开始就共享 UI。
- 让 common API 保持小而清晰。
- 平台能力通过接口或
expect/actual隔离。 - 依赖库进入
commonMain前确认它支持所有目标。 - 每个目标都要有基本构建和测试验证。
- Java/JVM 团队引入 KMP 前,先确认真的有非 JVM 目标需求。
参考¶
- 官方 Kotlin Multiplatform:https://kotlinlang.org/docs/multiplatform.html
- 官方入门:https://kotlinlang.org/docs/multiplatform/get-started.html