Kotlin Multiplatform:共享逻辑与 Compose Multiplatform
Kotlin Multiplatform(KMP)让一套 Kotlin 业务代码在 Android、iOS 等多端共享,UI 可以继续用各端原生写,也可以进一步用 Compose Multiplatform(CMP)连 UI 一起共享。理解 KMP 的第一件事:它不是 Flutter / React Native 那样的「一套 UI 处处跑」,而是「共享逻辑、保留原生」——想共享哪一层,决定你该怎么用它。
定位:共享什么,不共享什么
先在技术全景与选型里确认过结论:KMP 的正确姿势是共享业务逻辑层。具体分三层看:
| 层 | 是否共享 | 说明 |
|---|---|---|
| 业务逻辑(用例、规则、状态机) | ✅ 共享 | 语言无关的「做什么」,天然值得共享 |
| 数据层(网络、存储、领域模型) | ✅ 共享 | Ktor / SQLDelight / kotlinx.serialization 都有多平台实现 |
| UI 层 | ⚖️ 可选 | 默认各端原生 UI(Android = Jetpack Compose / XML,iOS = SwiftUI);想连 UI 一起共享再上 Compose Multiplatform |
一句话判断:
- 目标是「少写两遍业务代码、保留两端原生体验」→ KMP(逻辑共享 + 原生 UI);
- 目标是「一套 UI 代码跑两端、外观像素级一致」→ Flutter;
- 目标是「复用 React 生态、快速铺两端」→ React Native。
对比细节见 Flutter 与 React Native。KMP 的隐性前提是团队熟悉 Kotlin/JVM 生态(Kotlin 语言速览见 Android 开发),否则学习曲线是双份的。
源集与编译目标
KMP 一个模块可以同时面向多种目标平台:
- Android:编译为 JVM 字节码(作为 Android library 被 app 依赖);
- iOS:用 Kotlin/Native 编译为静态/动态 framework,嵌入 Xcode 工程被 Swift 调用;
- 桌面:macOS(JVM 或 Native)、Windows / Linux(JVM 或 Native);
- Web:JavaScript 与 WebAssembly(
wasmJs)。
代码按「源集」组织,依赖关系向下包含:
commonMain ← 共享代码:业务逻辑、接口、模型
├── androidMain ← Android 专属实现 / actual
└── iosMain ← iOS 专属实现 / actual(iosArm64 / iosSimulatorArm64 共享)依赖只会从「上层源集」流向「下层源集」:commonMain 里的代码只能用「所有目标平台都有」的 API,平台差异通过 expect / actual 桥接。
expect / actual:描述一次,各端实现
commonMain 里声明期望,每个平台源集里给实际实现:
// commonMain:只声明,不实现
expect fun currentPlatform(): String
// androidMain
actual fun currentPlatform(): String = "Android"
// iosMain
actual fun currentPlatform(): String = "iOS"使用约束:
expect修饰的函数 / 属性 / 类,每个已启用的目标源集都必须有对应actual,否则编译报错;- 官方库通常已提供
actual(如kotlinx.datetime、kotlin.time),日常业务代码真正要自己写expect/actual的场景不多——遇到差异先查库,再考虑自造; - 库作者可以用「默认实现 + 可覆盖」把多数平台兜住,减少使用方手写
actual的负担。
Gradle 工程与 iOS 集成
KMP 模块用 kotlin-multiplatform 插件声明目标与依赖:
// build.gradle.kts(KMP 共享模块;版本取 2026 当前稳定线,示例仅示意 API 形态)
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
}
kotlin {
androidTarget() // Android
listOf(iosArm64(), iosSimulatorArm64()).forEach { // iOS 真机 + 模拟器
it.binaries.framework {
baseName = "Shared" // 暴露给 Swift 的 framework 名
isStatic = true
}
}
sourceSets {
commonMain.dependencies {
implementation("io.ktor:ktor-client-core:3.x")
implementation("io.ktor:ktor-client-content-negotiation:3.x")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.x")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.x")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.x")
}
androidMain.dependencies {
implementation("io.ktor:ktor-client-okhttp:3.x") // Android 网络引擎
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:3.x") // iOS 网络引擎
}
}
}iOS 侧集成要点:
- Xcode 工程通过「Run Script」阶段调用 Gradle 生成 framework(官方模板里是
embedAndSignAppleFrameworkForXcode一类的封装),或用 CocoaPods / SPM 方式接入; - 模拟器与真机架构都要配:常见漏项是只配了
iosArm64,模拟器跑不起来; - framework 是编译产物,业务改动后要重新执行生成,开发时要让「改共享代码 → 重新生成 → Xcode 重新链接」的链路尽量短(或直接用 Compose Multiplatform 工程省掉手动集成)。
共享层依赖选型
共享层能做多深,取决于生态里有没有对应多平台库:
| 能力 | 常用选型 | 备注 |
|---|---|---|
| 网络 | Ktor Client | 各端选引擎:OkHttp(Android)/ Darwin(iOS)/ CIO / JS |
| 序列化 | kotlinx.serialization | 需 plugin.serialization,@Serializable 数据类 |
| 本地存储 | SQLDelight、Room(KMP)、DataStore(KMP) | SQLDelight 生成类型安全查询,多端一致 |
| 异步 / 响应式 | kotlinx.coroutines、Flow | 多端原生支持,是共享层的地基 |
| 依赖注入 | Koin、Kodein | 均有 multiplatform 支持 |
| 时间 / 平台能力 | kotlinx-datetime、官方平台库 | 尽量避免自己写 expect/actual |
一个共享网络层的最小例子:
// commonMain:共享仓库 + Ktor + kotlinx.serialization
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class Repo(val fullName: String, val description: String? = null)
fun createHttpClient() = HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
}
class RepoApi(private val client: HttpClient) {
suspend fun topRepos(org: String): List<Repo> =
client.get("https://api.github.com/orgs/$org/repos").body()
}UI 层只要把 RepoApi 当普通依赖使用即可:Android 侧包成 ViewModel + StateFlow(见 Android 开发 的 MVVM 部分),iOS 侧包成 ObservableObject / @Observable 供 SwiftUI 消费,业务判断全部复用上面的共享代码。
iOS 互操作:从 Swift 调共享代码
Kotlin/Native 与 Swift 互操作有几条要提前知道的现实:
- suspend 函数 → Swift async:Kotlin 的挂起函数在 Swift 侧可当 async 函数调用(编译链支持),异步衔接相对顺滑;
- Flow → Swift 需要转换:Flow 不会自动变成 Swift 的 AsyncSequence,通常用
AsyncStream手动桥接,或用 SKIE 这类工具自动生成更友好的 Swift API; - 内存模型纪律:Kotlin/Native 采用新内存模型后,对象不能随意跨线程共享。Swift 回调里把共享层对象塞进后台队列、或反过来在非主线程改共享层可变状态,是 iOS 集成里最常见的崩溃源;
- 顶层可变状态要克制:共享层尽量设计成「显式传依赖、不可变模型 + 单向数据流」,在 iOS 侧会少踩很多线程坑。
Compose Multiplatform:当你想连 UI 一起共享
Compose Multiplatform(CMP)把 Jetpack Compose 延伸到 iOS / 桌面 / Web:
- Android:与原生 Compose 同源,体验一致;
- iOS:编译为 Kotlin/Native,渲染走 Skia,用 UIKit 承载(官方状态以当前版本为准,移动端 iOS 支持已从预览走向稳定);
- 桌面:macOS / Windows / Linux 桌面 JVM 应用;
- Web:Wasm 目标。
在 KMP 基础上加 CMP 后,UI 层也可以共享(commonMain 写 Composable,各端只保留入口与平台配置)。判断是否值得:
| 信号 | 倾向 |
|---|---|
| 需要两端观感都「像素级一致」的自定义 UI | CMP |
| 产品有桌面 / Web 多端诉求,想一份 UI 通吃 | CMP |
| iOS 体验要深度贴合平台(手势、动效、系统组件习惯) | 原生 UI(SwiftUI + 共享逻辑 KMP) |
| UI 形态以列表 / 表单 / 内容为主,品牌一致性优先 | CMP 或原生皆可,按团队定 |
| 团队对 iOS 原生开发已很熟 | 原生 UI 更稳 |
CMP 不是「RN 的 Kotlin 版」:它不是用 WebView 或解释器跑 UI,而是 Compose 描述 UI 后由各自平台的渲染层绘制;但同时也要接受「共享 UI = 平台观感折中」,系统级细节(如 iOS 原生滚动回弹、导航过渡)仍可能需要逐端调或写平台分支。
常见坑速查
| 坑 | 现象 | 对策 |
|---|---|---|
| 期待 KMP 共享 UI,没上 CMP | 双端 UI 仍写两遍,以为选错了 | 先明确「共享逻辑层」与「共享 UI 层」是两档(CMP 才共享 UI) |
| iOS 只配真机 target | 模拟器链接失败 / 跑不起来 | iosSimulatorArm64(或 x86_64 旧机器)与真机都要配 |
忘加 plugin.serialization | @Serializable 报错 | 序列化编译器插件与 KMP 插件同版本引入 |
| Ktor 只加 core 没加引擎 | 构建通过但请求抛「找不到引擎」 | Android 加 OkHttp、iOS 加 Darwin、桌面/共用加 CIO 等 |
| iOS framework 集成链路过长 | 改了共享代码不生效,排查半天 | 自动化生成脚本,改代码后一键重建;或选 CMP 工程形态 |
| 共享层放顶层可变状态 | iOS 上偶发崩溃 / 数据错乱 | 新内存模型下不跨线程共享可变对象,用单向数据流 |
| 在 Swift 里直接 collect Flow | 编译不过 / 行为不符合预期 | 用 AsyncStream 或 SKIE 桥接 |
| expect/actual 到处自造 | 平台差异代码散落,难以维护 | 优先用官方多平台库,把差异收敛到少数封装点 |
| CMP 当 RN 用,期待动态热更 | 更新链路不符预期 | 先理解 CMP 是编译期原生 UI,发布走正常 App 审核链路 |
| 忽略 UI 层两端折中 | iOS 用户觉得「不够 iOS」 | 提前定视觉与交互基线,逐端打磨系统细节 |
检查清单
- [ ] 明确共享范围:逻辑层共享、UI 层是否走 CMP,结论写进方案;
- [ ] Android 与 iOS 目标、模拟器架构都在 Gradle 里配全;
- [ ] 网络 / 序列化 / 存储 / DI 依赖的引擎在各端源集都配好;
- [ ] 共享层遵守「不可变模型 + 单向数据流」,无顶层可变全局态;
- [ ] iOS 集成链路(framework 生成、嵌入 Xcode)脚本化,改动可复现;
- [ ] Swift 调 suspend / Flow 的桥接方案明确(async / AsyncStream / SKIE);
- [ ] 评估过 CMP 与原生 UI 的观感基线,两端一致性预期对齐;
- [ ] 排了 Kotlin / KMP / CMP 的升级预算,跟随官方路线图。
相关链接:客户端开发索引 · 技术全景与选型 · Android 开发 · Flutter · React Native · 桌面应用 · 鸿蒙开发。本页 Gradle / iOS 集成示例基于 2026 年当前工具链语境编写,需在 Android Studio + Xcode 环境中验证。