← 返回学习路线
🔮

第 1 章:KMP 简介与生态

了解 Kotlin Multiplatform 的核心理念、与竞品的本质区别,以及 JetBrains 为跨平台开发构建的完整生态

🔮 什么是 Kotlin Multiplatform

Kotlin Multiplatform(KMP)是 JetBrains 推出的跨平台开发技术,允许开发者使用 Kotlin 编写可在多个平台上运行的共享代码。与其他跨平台方案不同,KMP 的核心思路是共享业务逻辑,保留各平台原生 UI

2023 年 11 月,KMP 正式发布 Stable(稳定版)1.0,标志着该技术已达到生产就绪状态。Google 官方将其列为 Android 推荐的跨平台共享代码方案,Netflix、McDonald's、Cash App 等公司已在生产环境中使用 KMP。

KMP 不是"再造一个 Flutter" — KMP 不试图统一 UI 层,而是专注于让业务代码(网络请求、数据解析、数据库操作、业务规则)只写一次,各平台用自己最顺手的 UI 框架来展示。

1.1 KMP 解决了什么问题

在没有 KMP 之前,一个同时支持 Android 和 iOS 的团队需要维护两套完全独立的代码库:

  • 同样的 API 调用逻辑,用 Kotlin 写一遍,用 Swift 再写一遍
  • 同样的 JSON 解析模型,两端各自定义一次
  • 同样的业务规则(如价格计算、表单验证),两端分别实现,容易产生差异 bug
  • 同样的本地缓存策略,两端各自维护

KMP 将这些逻辑统一放在 shared 模块的 commonMain 中,Android 和 iOS 直接引用,从根本上消除了重复劳动和跨端不一致问题。

⚖️ 与竞品架构对比

理解 KMP 的最好方式是把它与 Flutter 和 React Native 进行架构层面的对比。

维度 Kotlin Multiplatform Flutter React Native
共享范围 业务逻辑(网络、数据库、状态) 业务逻辑 + UI(全部) 业务逻辑 + UI(大部分)
UI 渲染 各平台原生(Compose / SwiftUI) Skia/Impeller 自绘引擎 映射到平台原生组件
语言 Kotlin(共享)+ Kotlin/Swift(UI) Dart JavaScript / TypeScript
iOS 编译 原生机器码(Kotlin/Native) ARM 机器码(AOT) JavaScript(JSI 桥接)
原生感 完全原生 高度一致但非原生 接近原生
推进方 JetBrains + Google Google Meta
学习曲线 需同时了解 Android + iOS 学习 Dart 即可 JS 开发者较容易上手
适用场景 现有 Android/iOS 项目逐步共享 全新项目,追求 UI 高度一致 Web 团队转型移动开发

1.2 架构层次图

以下示意图展示了三种技术在架构层面的本质差异:

🔮 KMP 架构

┌─────────────┬─────────────┐
│ Android UI │ iOS UI │
│ (Compose) │ (SwiftUI) │
├─────────────┴─────────────┤
│ 共享业务逻辑 (Kotlin) │
│ 网络/数据库/状态/业务规则 │
└───────────────────────────┘

🎯 Flutter 架构

┌───────────────────────────┐
│ Flutter UI (Dart) │
│ Widgets / Skia 渲染引擎 │
├───────────────────────────┤
│ 业务逻辑 (Dart) │
├───────────────────────────┤
│ 平台通道 (MethodChannel)│
└───────────────────────────┘

1.3 KMP 的真正优势在哪里

✅ KMP 更适合的场景

  • 已有 Android 应用,需要扩展 iOS
  • 需要平台特定 UI/UX 设计
  • 团队同时有 Android 和 iOS 工程师
  • 对性能和平台一致性要求极高
  • 使用大量平台特定 API(相机、蓝牙等)

⚠️ KMP 需要权衡的场景

  • 全新项目且团队只会一门语言
  • 需要 UI 在所有平台高度一致
  • 团队没有 iOS/Swift 经验
  • 需要快速原型,时间非常紧张

🧩 核心理念:共享层 + 平台原生 UI

KMP 项目的典型代码组织方式如下图所示:

项目结构 // KMP 项目代码组织 MyApp/ ├── shared/ // 共享模块 │ └── src/ │ ├── commonMain/ // 所有平台共享的代码 │ │ └── kotlin/ │ │ ├── data/ // Repository, API, 数据模型 │ │ ├── domain/ // 业务用例, 领域逻辑 │ │ └── presentation/ // ViewModel, UI State │ ├── androidMain/ // Android 特有实现 │ └── iosMain/ // iOS 特有实现 │ ├── composeApp/ // Android UI (Jetpack Compose) │ └── src/androidMain/ │ └── iosApp/ // iOS UI (SwiftUI) └── iosApp.xcodeproj

1.4 expect / actual 机制

当某段代码在逻辑上是通用的,但底层实现依赖平台 API 时,KMP 提供了 expect/actual 机制。

expect:在 commonMain 中声明"我期望每个平台提供一个这样的实现"

actual:在 androidMain / iosMain 中提供真正的实现

commonMain — expect 声明 // shared/src/commonMain/kotlin/platform/Platform.kt @Platform expect class Platform { val name: String } // 获取当前平台名称 expect fun getPlatformName(): String // 生成 UUID(各平台实现不同) expect fun generateUUID(): String // 获取当前时间戳(毫秒) expect fun currentTimeMillis(): Long
androidMain — actual 实现 // shared/src/androidMain/kotlin/platform/Platform.android.kt actual class Platform { actual val name: String = "Android" } actual fun getPlatformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}" actual fun generateUUID(): String = java.util.UUID.randomUUID().toString() actual fun currentTimeMillis(): Long = System.currentTimeMillis()
iosMain — actual 实现 // shared/src/iosMain/kotlin/platform/Platform.ios.kt actual class Platform { actual val name: String = "iOS" } actual fun getPlatformName(): String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion() actual fun generateUUID(): String = NSUUID().UUIDString() actual fun currentTimeMillis(): Long = (NSDate().timeIntervalSince1970() * 1000).toLong()
规律: 文件命名约定是 ClassName.android.ktClassName.ios.kt,编译器会自动根据目标平台选择正确的文件。

🎯 支持的目标平台

KMP 支持将 Kotlin 代码编译到多个目标平台,每个平台使用不同的 Kotlin 编译器后端:

平台 编译器后端 运行时 成熟度
Android Kotlin/JVM ART (Android Runtime) ✅ 稳定
iOS Kotlin/Native 原生机器码 (ARM64) ✅ 稳定
macOS Kotlin/Native 原生机器码 ✅ 稳定
Windows Kotlin/Native 原生机器码 (mingw) ✅ 稳定
Linux Kotlin/Native 原生机器码 ✅ 稳定
Web (Wasm) Kotlin/Wasm WebAssembly 🔶 Beta
Web (JS) Kotlin/JS Node.js / Browser ✅ 稳定
JVM (桌面) Kotlin/JVM JVM ✅ 稳定
Kotlin/Native 的特殊性: iOS/macOS 使用 Kotlin/Native 编译,这意味着 Kotlin 代码被编译成原生机器码,可以调用 Objective-C / Swift 框架,但 GC 策略与 JVM 有差异(使用引用计数 + GC 混合模型),需要注意内存管理。

1.5 build.gradle.kts 中的目标声明

build.gradle.kts kotlin { // Android 目标 androidTarget { compilations.all { kotlinOptions { jvmTarget = "17" } } } // iOS 目标(真机 + 模拟器) iosX64() // iOS Simulator x86_64 iosArm64() // iOS 真机 iosSimulatorArm64() // Apple Silicon 模拟器 // 桌面端 jvm("desktop") // Web @OptIn(ExperimentalWasmDsl::class) wasmJs { browser() } sourceSets { commonMain.dependencies { // 所有平台共享的依赖 implementation(libs.kotlinx.coroutines.core) implementation(libs.ktor.client.core) } androidMain.dependencies { implementation(libs.ktor.client.okhttp) // Android 引擎 } iosMain.dependencies { implementation(libs.ktor.client.darwin) // iOS 引擎 } } }

🎨 Compose Multiplatform

Compose Multiplatform(CMP)是 JetBrains 在 KMP 基础上构建的 UI 共享层,将 Jetpack Compose 扩展到 iOS、桌面和 Web 平台。

🤖 Android

基于 Jetpack Compose,完全一致的 API,与 Android 生态无缝集成。

🍎 iOS (Beta)

使用 Skiko 渲染引擎在 iOS 上绘制 Compose UI,可访问 UIKit/SwiftUI 组件。

🖥️ Desktop (JVM)

基于 Skiko,在 macOS/Windows/Linux 桌面端运行完整 Compose 应用。

🌐 Web (Wasm)

通过 WebAssembly 在浏览器中运行 Compose UI,Alpha 阶段。

1.6 KMP 与 CMP 的关系

KMP 是基础,CMP 是可选的 UI 层:
- KMP:共享业务逻辑(必选)
- CMP:共享 UI 代码(可选,适合需要 UI 高度一致的项目)
- 两者可以共存:共享业务逻辑 + 部分共享 UI + 部分平台原生 UI
Compose Multiplatform — 共享 UI 示例 // composeApp/src/commonMain/kotlin/ui/HomeScreen.kt // 这段代码在 Android / iOS / Desktop 上完全一样 @Composable fun HomeScreen(viewModel: HomeViewModel = koinViewModel()) { val uiState by viewModel.uiState.collectAsState() LazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(uiState.articles) { article -> ArticleCard(article = article) } } }

🛠️ JetBrains 生态库

KMP 拥有一套经过专门适配的多平台库,这些库在 commonMain 中声明依赖,自动适配各目标平台:

  • Ktor Client JetBrains 官方 HTTP 客户端库,多平台支持。Android 使用 OkHttp 引擎,iOS 使用 Darwin 引擎,API 在 commonMain 中完全统一。支持 ContentNegotiation、Auth、WebSocket 等插件。
  • SQLDelight 根据 .sq SQL 文件自动生成类型安全的 Kotlin API。多平台驱动支持 Android (AndroidSqliteDriver)、iOS/Native (NativeSqliteDriver)、JVM (JdbcSqliteDriver)。Square 出品,与 KMP 深度集成。
  • Kotlinx.Serialization JetBrains 官方 Kotlin 序列化库,支持 JSON / CBOR / Protobuf 等格式,在 KMP 所有目标平台上均可使用,与 Ktor 无缝配合。
  • Kotlinx.Coroutines Kotlin 异步并发库,Flow / StateFlow / SharedFlow 等响应式原语,在 KMP 所有平台支持。iOS 需要注意主线程调度(使用 Dispatchers.Main)。
  • Kotlinx.DateTime 多平台日期时间库,替代 java.util.Date / NSDate,提供统一的 Instant、LocalDate、TimeZone API。
  • Koin 轻量级依赖注入框架,支持 KMP。在 commonMain 中声明模块,Android 使用 koin-android,iOS 调用 startKoin() 初始化。
  • Coil 3 Android 图片加载库 Coil 的第 3 代,完整支持 KMP(Android / iOS / Desktop / Web),替代 Glide/Picasso 做多平台图片加载。
  • SKIE Swift/Kotlin Interoperability Enhanced。让 iOS Swift 代码能以更自然的 Swift 风格使用 Kotlin 的 Flow、sealed class、coroutines,极大改善互操作体验。

📖 名词解释

  • commonMain KMP 源集(Source Set),包含在所有目标平台上都能运行的纯 Kotlin 代码。这里只能使用 Kotlin 标准库和明确支持多平台的三方库,不能调用 Android SDK 或 iOS SDK 的 API。
  • androidMain Android 平台专属源集,可以调用 Android SDK 的所有 API(Context、View、ActivityManager 等),也可以提供 expect 声明的 actual 实现。
  • iosMain iOS 平台专属源集,可以调用 Objective-C / iOS SDK 框架(UIKit、Foundation、AVFoundation 等),为 iOS 提供 actual 实现。
  • expect 在 commonMain 中声明一个"期望存在"的类、函数或属性,但不提供实现体。编译器强制要求每个目标平台提供对应的 actual 实现,否则编译报错。
  • actual 在平台特定源集(androidMain / iosMain)中提供 expect 声明的具体实现。actual 函数签名必须与 expect 完全匹配。
  • Kotlin/Native Kotlin 的原生编译器后端,将 Kotlin 代码编译为原生机器码,无需 JVM 运行时。iOS、macOS、Linux、Windows 目标使用此后端,编译结果可以作为原生库被 Swift/C 代码调用。
  • XCFramework Apple 的通用框架格式,可以包含多种架构(arm64 真机 + x86_64/arm64 模拟器)的编译产物。KMP 将 shared 模块发布为 XCFramework,供 Xcode 项目引用。
  • Source Set KMP 中的"代码集"概念,每个 Source Set 针对特定平台或平台组合。KMP 的 Source Set 层次结构是:commonMain → 中间集(如 appleMain)→ 平台集(iosMain / macosMain)。

🎯 本章小结

学完本章,你应该能够回答以下问题:

  1. KMP 与 Flutter 最本质的区别是什么?(提示:共享 UI vs 共享业务逻辑)
  2. expect / actual 机制解决了什么问题?
  3. 为什么说 KMP 适合"渐进式迁移"?
  4. Compose Multiplatform 与 KMP 是什么关系?