Kotlin Multiplatform 完全指南:从入门到实战(2026)

3 阅读5分钟

Kotlin Multiplatform 完全指南:从入门到实战(2026)

Kotlin Multiplatform (KMP) 是 2025-2026 年最热门的跨平台技术,它让你共享业务逻辑代码,同时保留各平台的原生 UI 体验。本文从零开始,手把手带你掌握 KMP 的核心概念、项目搭建、网络层、数据持久化,以及最终的多端部署。


一、什么是 Kotlin Multiplatform?

Kotlin Multiplatform (KMP) 是 JetBrains 推出的逻辑跨平台方案:

共享代码 (Kotlin/Common)
├── 网络请求 (Ktor)
├── 数据持久化 (SQLDelight)
├── 业务逻辑 / ViewModel
├── 序列化 (kotlinx.serialization)
└── 工具类 / 扩展函数
        ↓           ↓           ↓
   Android      iOS      Desktop/Web
 (原生 UI)   (原生 UI)  (Compose MP)

与 Flutter / React Native 的核心区别:

对比维度KMPFlutterReact Native
UI 渲染各平台原生 UIFlutter 自绘原生 UI
性能原生级接近原生依赖 JS Bridge
共享范围业务逻辑全部(UI + 逻辑)全部(UI + 逻辑)
学习曲线中(需懂各平台)低(一套代码)中(需懂各平台)
适用场景已有原生项目想共享逻辑从零开始的全跨平台轻量级跨平台

二、环境搭建(2026 最新)

2.1 软件要求

工具版本要求说明
Android Studio2024.1.1+需安装 KMP 插件
Xcode15.0+iOS 编译依赖
Kotlin2.0.0+支持 K2 编译器
Gradle8.5+需配合 Kotlin 版本

2.2 安装 KMP 插件

打开 Android Studio:

Settings → Plugins → Marketplace
搜索 "Kotlin Multiplatform"
安装并重启

2.3 验证环境

# 检查 Kotlin 版本
kotlin -version

# 检查 Xcode 命令行工具
xcode-select -p
# 应输出:/Applications/Xcode.app/Contents/Developer

# 初始化 KMP 项目(命令行方式)
kotlin multiplatform init

三、创建第一个 KMP 项目

3.1 使用 Android Studio 创建

File → NewNew Project
选择 "Kotlin Multiplatform" 模板
填写:
  - Project name: "KMPDemo"
  - Package name: "com.example.kmpdemo"
  - Target platforms: ✅ Android ✅ iOS ✅ Desktop

生成的项目结构:

KMPDemo/
├── composeApp/              # Compose Multiplatform(共享 UI,可选)
│   └── src/
│       ├── androidMain/    # Android 特定代码
│       ├── iosMain/        # iOS 特定代码
│       ├── desktopMain/   # Desktop 特定代码
│       └── commonMain/    # 共享代码 ⭐ 核心
│
├── iosApp/                # iOS 原生项目(Xcode 管理)
│   └── iosApp.xcodeproj
│
├── shared/                # 共享业务逻辑模块 ⭐ 核心
│   └── src/
│       ├── androidMain/    # Android 实际实现
│       ├── iosMain/        # iOS 实际实现(通过 Objective-C 桥接)
│       ├── commonMain/     # 期望声明 + 共享实现
│       └── nativeMain/    # 原生代码(iOS/Android 共用)
│
└── build.gradle.kts       # 根构建文件

四、shared 模块详解(核心)

4.1 commonMain:编写共享代码

// shared/src/commonMain/kotlin/AppConfig.kt
object AppConfig {
    const val APP_NAME = "KMPDemo"
    const val VERSION = "1.0.0"
}

// shared/src/commonMain/kotlin/Platform.kt
interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

4.2 androidMain / iosMain:平台特定实现

// shared/src/androidMain/kotlin/Platform.android.kt
actual fun getPlatform(): Platform = object : Platform {
    override val name: String
        get() = "Android ${android.os.Build.VERSION.SDK_INT}"
}
// shared/src/iosMain/kotlin/Platform.ios.kt
import platform.UIKit.UIDevice

actual fun getPlatform(): Platform = object : Platform {
    override val name: String
        get() = "iOS ${UIDevice.currentDevice.systemVersion}"
}

4.3 在 Android 中使用共享代码

// androidApp/src/main/kotlin/MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val platform = getPlatform()
        println("Running on: ${platform.name}")
        // 输出:Running on: Android 35

        setContent {
            Text("Hello from KMP! Platform: ${platform.name}")
        }
    }
}

五、网络层实战:Ktor Client

5.1 添加依赖

// shared/build.gradle.kts
kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                // Ktor 客户端(跨平台网络库)
                implementation("io.ktor:ktor-client-core:3.0.0")
                implementation("io.ktor:ktor-client-content-negotiation:3.0.0")
                implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.0")
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-android:3.0.0")
                implementation("io.ktor:ktor-client-content-negotiation:3.0.0")
            }
        }
        val iosMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-darwin:3.0.0")
            }
        }
    }
}

5.2 封装跨平台 HTTP 客户端

// shared/src/commonMain/kotlin/network/HttpClientFactory.kt
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.serialization.kotlinx.json.*

fun createHttpClient(): HttpClient {
    return HttpClient {
        // 自动序列化 / 反序列化 JSON
        install(ContentNegotiation) {
            kotlinx.serialization.json()
        }
        // 日志(Debug 模式)
        install(Logging) {
            logger = Logger.DEFAULT
            level = LogLevel.INFO
        }
    }
}

5.3 数据模型定义(commonMain)

// shared/src/commonMain/kotlin/model/User.kt
import kotlinx.serialization.Serializable

@Serializable
data class User(
    val id: Long,
    val name: String,
    val email: String,
    val avatarUrl: String? = null
)

@Serializable
data class ApiResponse<T>(
    val code: Int,
    val message: String,
    val data: T
)

5.4 仓库层(Repository)

// shared/src/commonMain/kotlin/repository/UserRepository.kt
class UserRepository {
    private val httpClient = createHttpClient()
    private val baseUrl = "https://api.example.com"

    suspend fun getUsers(): List<User> {
        return try {
            httpClient.get("$baseUrl/users")
                .body<ApiResponse<List<User>>>()
                .data
        } catch (e: Exception) {
            println("Error fetching users: ${e.message}")
            emptyList()
        }
    }

    suspend fun getUserById(id: Long): User? {
        return try {
            httpClient.get("$baseUrl/users/$id")
                .body<ApiResponse<User>>()
                .data
        } catch (e: Exception) {
            null
        }
    }
}

六、数据持久化:SQLDelight

6.1 添加依赖

// shared/build.gradle.kts
plugins {
    id("app.cash.sqldelight") version "2.0.0"
}

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:runtime:2.0.0")
                implementation("app.cash.sqldelight:coroutines-extensions:2.0.0")
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:android-driver:2.0.0")
            }
        }
        val iosMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:native-driver:2.0.0")
            }
        }
    }
}

6.2 定义 SQL 模式

-- shared/src/commonMain/sqldelight/com/example/kmpdemo/db/User.sq

CREATE TABLE UserEntity (
    id INTEGER NOT NULL,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    avatarUrl TEXT,
    PRIMARY KEY(id)
);

insertUser:
INSERT INTO UserEntity(id, name, email, avatarUrl)
VALUES (?, ?, ?, ?);

getUserById:
SELECT * FROM UserEntity WHERE id = ?;

getAllUsers:
SELECT * FROM UserEntity;

searchUsersByName:
SELECT * FROM UserEntity WHERE name LIKE '%' || ? || '%';

6.3 使用 SQLDelight(commonMain)

// shared/src/commonMain/kotlin/db/DatabaseDriverFactory.kt
expect class DatabaseDriverFactory {
    fun createDriver(): SqlDriver
}

// shared/src/commonMain/kotlin/repository/UserLocalRepository.kt
class UserLocalRepository(private val driverFactory: DatabaseDriverFactory) {
    private val database = AppDatabase(driverFactory.createDriver())

    suspend fun insertUser(user: User) {
        database.userQueries.insertUser(
            id = user.id,
            name = user.name,
            email = user.email,
            avatarUrl = user.avatarUrl
        )
    }

    suspend fun getAllUsers(): List<UserEntity> {
        return database.userQueries.getAllUsers().executeAsList()
    }
}

七、在 iOS 项目中使用共享代码

7.1 配置 iOS 框架

// shared/build.gradle.kts
kotlin {
    ios {
        binaries {
            framework {
                baseName = "shared"
                // 导出需要暴露给 Swift 的类
                export("app.cash.sqldelight:runtime:2.0.0")
            }
        }
    }
}

7.2 Swift 中调用共享代码

// iosApp/iosApp/ContentView.swift
import SwiftUI
import shared

struct ContentView: View {
    @State private var users: [User] = []

    var body: some View {
        List(users, id: \.id) { user in
            VStack(alignment: .leading) {
                Text(user.name).font(.headline)
                Text(user.email).font(.subheadline).foregroundColor(.gray)
            }
        }
        .onAppear {
            loadUsers()
        }
    }

    func loadUsers() {
        let repository = UserRepository()
        repository.getUsers(completionHandler: { users, error in
            if let users = users {
                self.users = users
            }
        })
    }
}

八、KMP + Compose Multiplatform(共享 UI)

⚠️ 注意:Compose Multiplatform 目前(2026 年初)已支持 Android + Desktop,iOS 支持处于 Beta 阶段,生产环境需谨慎。

8.1 添加 Compose Multiplatform 依赖

// composeApp/build.gradle.kts
plugins {
    id("org.jetbrains.compose")
    id("com.android.application")
}

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)
            }
        }
    }
}

8.2 共享 UI 代码

// composeApp/src/commonMain/kotlin/App.kt
import androidx.compose.material3.*
import androidx.compose.runtime.*

@Composable
fun App() {
    MaterialTheme {
        var greeting by remember { mutableStateOf("Hello from KMP!") }
        val platform = getPlatform()

        Scaffold(
            topBar = {
                TopAppBar(title = { Text("KMP Demo") })
            }
        ) { padding ->
            Column(
                modifier = Modifier.padding(padding),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(greeting)
                Text("Platform: ${platform.name}")
                Button(onClick = {
                    greeting = "You clicked the button!"
                }) {
                    Text("Click me")
                }
            }
        }
    }
}

九、实战避坑指南

9.1 iOS 框架编译失败

问题ld: framework not found shared

解决方案

1. 在 Xcode 中:Target → Build Settings → Framework Search Paths
   添加: $(SRCROOT)/../shared/build/bin/iosArm64/debugFramework

2. 确保每次 Android Studio 编译后,运行:
   ./gradlew :shared:assembleSharedXCFramework

9.2 泛型在 Swift 中不可见

Kotlin 的泛型在导出到 Swift 时会丢失类型信息。

解决方案:使用 typealias 或接口包装:

// 在 shared 模块中
class UserRepositoryIos : UserRepository() {
    // 提供非泛型方法给 Swift 调用
    fun getUsersIos(completion: (List<User>) -> Unit) {
        // ...
    }
}

9.3 SQLDelight 在 iOS 上崩溃

问题NSInternalInconsistencyException

原因:iOS 上数据库文件路径未正确配置。

解决方案

// shared/src/iosMain/kotlin/DatabaseDriverFactory.ios.kt
actual class DatabaseDriverFactory {
    actual fun createDriver(): SqlDriver {
        val documentDirectory = NSFileManager.defaultManager
            .URLForDirectory(
                directory = NSDocumentDirectory,
                inDomain = NSUserDomainMask,
                appropriateForURL = null,
                create = false,
                error = null
            )
        val dbFilePath = documentDirectory?.path + "/$DB_NAME"
        return NativeSqliteDriver(AppDatabase.Schema, dbFilePath)
    }
}

十、发布与部署

10.1 Android 打包

# 生成 APK
./gradlew :androidApp:assembleRelease

# 生成 AAB(Google Play 推荐)
./gradlew :androidApp:bundleRelease

10.2 iOS 打包

1. 在 Xcode 中打开 iosApp/iosApp.xcworkspace
2. 选择 Target → Any iOS Device (arm64)
3. Product → Archive
4. 上传到 App Store Connect

十一、KMP 适用场景判断

场景推荐方案理由
已有 Android + iOS 原生项目,想共享逻辑✅ KMP渐进式迁移
从零开始做跨平台 App⚠️ KMP 或 Flutter看团队技能栈
需要高性能游戏 / 图形❌ KMP不够适合
企业级应用(重业务逻辑)✅ KMP共享业务逻辑优势明显
初创公司快速验证 MVP⚠️ Flutter 更快一套代码跑全平台

十二、学习资源

资源链接
官方文档kotlinlang.org/docs/multip…
Ktor 文档ktor.io/docs/
SQLDelight 文档cashapp.github.io/sqldelight/
Compose Multiplatformwww.jetbrains.com/lp/compose-…
KMP 示例项目github.com/Kotlin/kmm-…

总结

Kotlin Multiplatform 的核心价值是共享业务逻辑,保留原生 UI 体验。2026 年它已趋于成熟,适合:

  1. Android 开发者想扩展到 iOS,但不想放弃原生 UI
  2. 企业级应用,业务逻辑复杂,需要跨平台共享
  3. 渐进式迁移,从共享网络层、数据层开始

下一步学习路径:

KMP 基础 → Ktor 网络层 → SQLDelight 数据持久化
    ↓
Compose Multiplatform(共享 UI,可选)
    ↓
发布到 Google Play + App Store

如果本文对你有帮助,欢迎点赞 + 收藏,后续会持续更新 KMP 实战系列文章。