从 0 到 1 快速上手 KMP
Kotlin Multiplatform (KMP) 不再是未来,而是当下。它允许您在保持 100% 原生性能的同时,在 Android、iOS、Desktop 和 Web 之间共享核心业务逻辑。
环境准备清单
在开始之前,请确保您的开发机器满足以下要求:
- JDK: 版本 17 或更高。
- Android Studio: 推荐 Koala (2024.1.1) 或更高版本,并安装 Kotlin Multiplatform 插件。
- Xcode: (仅 iOS 开发需要) 建议最新稳定版。
- Kotlin: 本教程基于 Kotlin 2.3.0。
自动化检查工具
推荐运行 kdoctor 工具来诊断环境问题:
bash
brew install kdoctor
kdoctor创建首个 KMP 项目
最简单的方式是使用官方的 KMP Wizard。
- 选择目标平台:Android, iOS (Share UI via Compose) 或 iOS (Native UI), Desktop。
- 输入项目名称和包名。
- 下载生成的项目压缩包并解压。
- 在 Android Studio 中打开
build.gradle.kts。
项目骨架拆解
一个典型的 KMP 项目(以 shared 模块为核心)结构如下:
shared/src/commonMain: 核心共享代码(逻辑、数据模型、API 调用)。shared/src/androidMain: Android 特有的 API 实现。shared/src/iosMain: iOS 特有的 API 实现(可直接调用 Apple 框架)。composeApp: (如果选择了 Compose Multiplatform) 共享 UI 代码。
核心配置:build.gradle.kts
在 shared 模块中,关键配置如下:
kotlin
kotlin {
// 1. 定义目标平台
androidTarget()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "SharedFramework"
isStatic = true
}
}
// 2. 配置源集依赖
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}
androidMain.dependencies {
// Android 专用库
}
iosMain.dependencies {
// iOS 专用库
}
}
}跨平台运行
bash
# 在 AS 中直接点击 Run 按钮,或者:
./gradlew :composeApp:installDebugbash
# 需要在 AS 中配置 iOS 模拟器运行项,
# 或者在 Xcode 中打开 iosApp 目录。bash
./gradlew :composeApp:run关键能力详解
本节围绕 shared 模块的关键能力展开,内容遵循 API 核心签名说明 → 标准代码块 → 底层机制说明 的结构,便于直接落地。
源集与 expect/actual 设计
API 核心签名说明
expect fun platformName(): Stringactual fun platformName(): String
标准代码块
kotlin
// commonMain
expect fun platformName(): String
class Greeting {
fun greeting(): String = "Hello, ${platformName()}!"
}
// androidMain
actual fun platformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"
// iosMain
import platform.UIKit.UIDevice
actual fun platformName(): String = UIDevice.currentDevice.systemName协程调度与跨平台主线程
API 核心签名说明
interface CoroutineScopefun CoroutineScope.launch(block: suspend CoroutineScope.() -> Unit): Jobsuspend fun <T> withContext(context: CoroutineContext, block: suspend () -> T): Tobject Dispatchers { val Main: CoroutineDispatcher; val Default: CoroutineDispatcher }
标准代码块
kotlin
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class UserPresenter(
private val mainDispatcher: CoroutineDispatcher,
private val ioDispatcher: CoroutineDispatcher,
private val repo: UserRepository,
) {
private val scope = CoroutineScope(SupervisorJob() + mainDispatcher)
fun refresh() {
scope.launch {
val user = withContext(ioDispatcher) { repo.fetch() }
onUserLoaded(user)
}
}
fun clear() {
scope.cancel()
}
private fun onUserLoaded(user: User) { /* 平台层实现 */ }
}
// androidMain
fun createPresenter(repo: UserRepository) = UserPresenter(
mainDispatcher = Dispatchers.Main.immediate,
ioDispatcher = Dispatchers.IO,
repo = repo
)
// iosMain
fun createPresenter(repo: UserRepository) = UserPresenter(
mainDispatcher = Dispatchers.Main,
ioDispatcher = Dispatchers.Default,
repo = repo
)依赖补充
kotlin
kotlin {
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
androidMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
}
}
}groovy
kotlin {
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0"
}
}
androidMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
}
}
}
}toml
[versions]
kotlinx-coroutines = "1.9.0"
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }最新稳定版本查看链接:https://github.com/Kotlin/kotlinx.coroutines/releases
序列化与跨平台数据模型
API 核心签名说明
@Serializableinterface KSerializer<T>fun Json.encodeToString(value: T): Stringfun Json.decodeFromString(string: String): T
标准代码块
kotlin
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class User(
val id: String,
val name: String,
)
private val json = Json {
ignoreUnknownKeys = true
}
fun encodeUser(user: User): String = json.encodeToString(user)
fun decodeUser(payload: String): User = json.decodeFromString(payload)依赖补充
kotlin
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
}
kotlin {
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}
}
}groovy
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
}
}
}
}toml
[versions]
kotlin = "2.3.0"
kotlinx-serialization = "1.7.3"
[libraries]
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }最新稳定版本查看链接:https://github.com/Kotlin/kotlinx.serialization/releases
原生框架导出与集成
API 核心签名说明
binaries.framework { baseName: String; isStatic: Boolean }freeCompilerArgs += listOf("-Xobjc-generics")
标准代码块
kotlin
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
targets.withType<KotlinNativeTarget>().configureEach {
binaries.framework {
baseName = "SharedKit"
isStatic = true
freeCompilerArgs += listOf("-Xobjc-generics")
}
}
}跨平台测试与验证
API 核心签名说明
@Testkotlin.test.assertEquals(expected: Any?, actual: Any?)kotlin.test.assertTrue(actual: Boolean)kotlin.test.assertFailsWith<T : Throwable>(block: () -> Unit)
标准代码块
kotlin
import kotlin.test.Test
import kotlin.test.assertTrue
class GreetingTest {
@Test
fun greetingHasPlatformName() {
val message = Greeting().greeting()
assertTrue(message.isNotBlank())
}
}kotlin
import kotlin.test.Test
import kotlin.test.assertTrue
class AndroidPlatformTest {
@Test
fun platformNameContainsAndroid() {
val name = platformName()
assertTrue(name.contains("Android"))
}
}kotlin
import kotlin.test.Test
import kotlin.test.assertTrue
class IosPlatformTest {
@Test
fun platformNameContainsSystemName() {
val name = platformName()
assertTrue(name.isNotBlank())
}
}依赖补充
kotlin
kotlin {
sourceSets {
commonTest.dependencies {
implementation(kotlin("test"))
}
androidUnitTest.dependencies {
implementation(kotlin("test-junit"))
}
}
}groovy
kotlin {
sourceSets {
commonTest {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-test:2.3.0"
}
}
androidUnitTest {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-test-junit:2.3.0"
}
}
}
}toml
[versions]
kotlin = "2.3.0"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }最新稳定版本查看链接:https://github.com/JetBrains/kotlin/releases
常见新手坑位
- Cocoapods 冲突:
推荐优先使用 Apple Framework 导出模式,除非必须集成大量遗留 Pods。
- 资源访问: KMP 中访问图片和字符串资源需要使用专门的库(如
Compose Multiplatform Resources)。 - 编译速度: 首次构建 iOS 框架可能较慢,这是因为 Kotlin/Native 编译器正在进行 LLVM 优化。