Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. 盘点纯业务, 协议, 算法与 Android 特有能力.
  2. 先抽 common interface 和可测试模型, 再迁移实现.
  3. 平台能力使用小接口注入, 避免 commonMain 出现平台类型.
  4. 为 iOS 提供 Swift 友好 facade, 设计取消, 错误和线程语义.
  5. CI 同时跑 commonTest, Android 测试, iOS framework 构建和最小集成 demo.
  6. 记录包体, 构建时间, 崩溃符号化和版本兼容成本, 分阶段扩大共享范围.

高频面试题

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.

版本与参考资料