Kotlin Multiplatform
KMP 适合 “共享可验证的业务逻辑, 保留平台能力与原生体验”.具体 API 的稳定性不同, 不能把 KMP 整体稳定与某个实验性互操作功能混为一谈.
一, 共享边界
优先共享领域模型, 业务规则, 协议, 序列化, 网络编排和算法. 平台 UI, 权限, 存储, 安全硬件, 后台执行和系统集成通常保留平台实现或建立窄接口.
| 层 | 共享建议 | 风险 |
|---|---|---|
| domain/model | 高 | 公共模型需兼顾 Swift/Objective-C 暴露 |
| data/network | 中高 | 取消, 异常, 缓存和线程模型要跨端统一 |
| platform capability | 通过接口适配 | 不把 Context/Keychain/Keystore 泄漏到 commonMain |
| UI | 按项目选择 | 原生 UI 与 Compose Multiplatform 的团队成本不同 |
二, expect / actual 的稳定性
expect/actual 函数和属性可用于平台能力连接, 但 expect/actual classes 截至 2026-08-07 仍为 Beta. 公共 SDK 不应只因为语法方便就大面积暴露 expect/actual class; 优先考虑 common interface + 平台实现注入.
// commonMain
interface DeviceInfoProvider {
fun deviceModel(): String
}
// androidMain 通过构造注入到 common 代码
class AndroidDeviceInfoProvider : DeviceInfoProvider {
override fun deviceModel(): String = android.os.Build.MODEL
}
如使用 expect/actual class, 应固定 Kotlin 版本, 记录 Beta 状态并准备迁移边界.
三, Source sets 与依赖
shared
├─ commonMain # domain,协议,Ktor,序列化,算法
├─ androidMain # Android Context,Keystore,Room 适配
├─ iosMain # Keychain,Foundation/平台能力适配
└─ commonTest # 共享业务规则测试
库选择要同时评估 Android/iOS 调试, 包体积, 二进制兼容, 构建缓存, 维护状态和团队 owner, 不能只看 “支持 KMP” 标签.
最小工程: 共享平台名
以下是教学骨架, 锁定 AGP 8.x 的经典 androidTarget() 基线; 截至 2026-08-07 此 DSL 仍受支持. 前提是工程已应用与版本匹配的 kotlin("multiplatform") 插件, Android App 已配置 SDK; 版本号由项目的 version catalog 或插件管理统一提供. 未在本仓库执行构建.
Google 的新路线使用 com.android.kotlin.multiplatform.library, 但 DSL 必须随锁定的 AGP 版本核验, 不能把整个 AGP 9.x 概括为 androidLibrary {}: AGP 8.12+ 的新插件使用 kotlin { android { ... } }; androidLibrary {} 在 AGP 9.1 alpha 后已弃用. 迁移新插件时, 按目标版本的 Android KMP 插件文档 与 AGP release notes 选择 DSL; 本文不提供容易随预览版变化而失效的可复制新插件配置.
// shared/build.gradle.kts
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64()).forEach { target ->
target.binaries.framework {
baseName = "Shared" // Swift 侧 `import Shared` 使用的 framework 名
isStatic = true
}
}
sourceSets {
commonMain.dependencies { }
androidMain.dependencies { }
iosMain.dependencies { }
}
}
// commonMain/kotlin/Platform.kt
expect fun platformName(): String
class Greeting {
fun greet(): String = "Hello from ${platformName()}"
}
// androidMain/kotlin/Platform.android.kt
actual fun platformName(): String = "Android"
// iosMain/kotlin/Platform.ios.kt
actual fun platformName(): String = "iOS"
Android 可从 Activity 调用 Greeting().greet(). 声明 binaries.framework 后, 才会生成 :<module>:embedAndSignAppleFrameworkForXcode 任务; Xcode 工程需在 Run Script 中调用该任务, 随后才能链接 / 嵌入 framework. 构建生成 framework 并由 Xcode 工程链接后, Swift 侧的调用上下文片段为:
import Shared
let text = Greeting().greet() // 预期为 "Hello from iOS"
若 iOS 无法 import Shared, 先检查 framework 是否已加入 target 的 Frameworks, Search Paths 和 Embed/Sign 配置; 若 actual 缺失, Gradle 会在对应 target 编译时报告 expect/actual 不匹配. 不要在 commonMain 引入 android.* 或 UIKit: 应以接口或 expect/actual 保持边界. 本篇讲 KMP 工程与互操作, 不比较框架优劣; 选型流程见 跨端技术对比.
四, iOS 导出与 Swift 互操作
KMP 可生成 framework / 库供 iOS 使用, 但 Objective-C export, Swift interop 和 Swift export 的能力与稳定性不同.Swift export 截至核验日仍为 Experimental, 不应写成生产环境无条件稳定方案.
suspend可桥接 async/callback, 但要统一取消和错误映射.- 不直接把复杂 Flow 类型泄漏给 Swift; 可提供观察接口, AsyncSequence 风格 facade 或显式订阅句柄.
- 泛型, sealed hierarchy, 异常和默认参数在 Swift 侧不一定自然, 应以实际生成 API 为准.
- CI 必须构建 iOS framework/Swift 调用 demo, 不能只跑 commonTest.
五, 与 Flutter/React Native 的选择
KMP 的典型价值是共享逻辑并保留平台 UI; Flutter/React Native 更偏统一 UI 技术栈. 选择时使用同一场景比较: 交付速度, UI 一致性, 平台 API 深度, 性能证据, 包体, 调试, 招聘和长期 owner, 而不是给框架做无来源性能排名. 七种方案的横向对比与统一场景 PoC 流程见 见 48.
六, Android SDK 迁移到 KMP
- 盘点纯业务, 协议, 算法与 Android 特有能力.
- 先抽 common interface 和可测试模型, 再迁移实现.
- 平台能力使用小接口注入, 避免 commonMain 出现平台类型.
- 为 iOS 提供 Swift 友好 facade, 设计取消, 错误和线程语义.
- CI 同时跑 commonTest, Android 测试, iOS framework 构建和最小集成 demo.
- 记录包体, 构建时间, 崩溃符号化和版本兼容成本, 分阶段扩大共享范围.
高频面试题
Q1: KMP 适合什么场景?
适合共享规则稳定, 可测试且平台差异可被窄接口隔离的逻辑. 高度依赖平台 UI / 系统能力且团队没有跨端 owner 时要谨慎.
Q2: expect/actual 是否全部稳定?
不能笼统回答. 具体声明形态稳定性不同; 截至 2026-08-07, expect/actual classes 仍为 Beta, 应按 Kotlin 版本核验.
Q3: Swift export 可以直接用于稳定公共 SDK 吗?
截至核验日仍是 Experimental, 应先验证生成 API, 二进制兼容和迁移成本, 不作为无条件承诺.
易错点 / 追问
- 不说 “一套代码无成本运行所有端”.
- 不把平台差异强行塞进 commonMain.
- 不把 Beta/Experimental 功能包装成稳定 API.
- 不忽略 iOS 调试, 符号化, 构建和团队 ownership.