介绍
Kotlin 多平台(KMP,Kotlin Multiplatform)是 JetBrains 的一项开源技术,采用”共享代码、原生实现”的架构理念,能够在 Android、iOS、桌面、网页和服务器之间共享代码,同时保留原生开发的优势。与传统的”一次编写,到处运行”虚拟机方案不同,Kotlin Multiplatform通过LLVM编译器直接将共享代码分别编译为目标平台的原生代码,确保了与平台SDK的深度集成和最优性能,实现了代码复用性与原生性能的完美平衡
同时你还可以通过 Compose Multiplatform,在多个平台共享 UI 代码,最大化代码的重复使用。
核心优势
- 代码共享最大化:业务逻辑、数据模型、网络层等核心代码100%共享
- 原生性能:直接编译为平台原生代码,无中间虚拟机开销
- 平台特性访问:通过预期声明(Expect/Actual)机制无缝调用平台API
- 类型安全:全平台统一的类型系统,编译时捕获跨平台兼容性问题
- 工具链集成:与Android Studio和Xcode深度集成,提供一流开发体验
工具使用:
IntelliJ IDEA 和 Android Studio 为 KMP 提供了智能 IDE 支持,包含 Kotlin 多平台 IDE 插件、通用界面预览、Compose 多平台热重载、跨语言导航、重构以及跨 Kotlin 和 Swift 代码的调试
项目架构最佳实践
推荐的模块结构
一个典型的KMP项目应包含以下模块结构,这种结构既能最大化代码共享,又能清晰分离平台特定代码:
kotlin-multiplatform-project/├── gradle/ # Gradle 配置│ ├── libs.versions.toml # 版本目录│ └── wrapper/├── shared/ # 共享模块│ ├── build.gradle.kts # KMP 插件配置│ └── src/│ ├── commonMain/ # 通用代码│ │ └── kotlin/│ │ ├── domain/ # 业务逻辑│ │ ├── data/ # 数据层│ │ └── di/ # 依赖注入│ ├── androidMain/ # Android 特定│ ├── iosMain/ # iOS 特定│ └── commonTest/ # 通用测试|—— composeApp/ # compose 共享模块| ├── build.gradle.kts # compose 模块构建配置| └── src/| ├── commonMain/| │ └── kotlin/| │ ├── ui/ # Compose UI 组件和页面| │ │ ├── screens/| │ │ └── components/| │ ├── theme/ # 主题定义(颜色、字体、形状)| │ └── navigation/ # 导航路由| ├── androidMain/ # Android 入口| │ └── kotlin/| └── iosMain/ # iOS 入口| └── kotlin/||—— server/| ├── build.gradle.kts # 服务端构建配置(Ktor Framework)| └── src/| └── main/| └── kotlin/| └── com/yourpackage/| ├── Application.kt # Ktor 应用入口| ├── routes/ # API 路由定义| ├── plugins/ # Ktor 插件配置| └── services/ # 业务服务层|├── androidApp/ # Android 应用│ └── src/main/│ └── AndroidManifest.xml└── iosApp/ # iOS 应用 └── iosApp/可以直接使用 AndroidStudio 或者 IDEA的模板, 新建项目时,选择 Phone and Tablet 中的 Kotlin Multiplatform 模板进行项目创建
共享代码组织原则
- 按功能划分包结构:优先按业务功能而非技术层次组织代码
- 平台相关代码隔离:使用expect/actual机制最小化平台特定代码
- 依赖注入解耦:通过接口抽象平台特定实现
- 资源共享策略:使用JSON或XML定义共享资源,平台负责本地化
配置介绍
通用代码
通用代码是不同平台共享的Kotlin代码,通常位于平台间共享的目录中。代码文件的位置很重要,因为它会影响代码编译到的平台列表。
Kotlin 编译器接收源代码作为输入,并生成一组平台特定的二进制文件。在编译多平台项目时,它可以从同一代码生成多个二进制文件。例如,编译器可以从同一个Kotlin文件生成JVM文件和本地可执行文件:.class
并非所有 Kotlin 代码都能编译到所有平台。如果代码无法编译到其他平台,Kotlin编译器会阻止你在通用代码中使用平台特定的函数或类
在通用代码中,你可以使用Kotlin多平台库。这些库提供了一个通用的API,可以在不同平台上以不同方式实现。在这种情况下,平台特定的 API 作为额外部分
编译目标
目标定义了Kotlin编译通用代码的平台。这些可以是JVM、JS、Android、iOS或Linux
在 Gradle 中,你在块内使用预定义的 DSL 调用来声明目标,指示Kotlin为该目标编译代码。每个多平台项目都能定义一组支持的目标
kotlin { jvm() iosArm64() androidTarget()}声明一个编译目标时,Kotlin Gradle 插件会自动为这个目标生成对应的源集,如 jvm 自动生成 jvmMain、jvmTest 源集,commonMain 源集不用声明,gradle 插件会自动生成
源集(sourceSets)
Kotlin 源集是一组具有自身目标、依赖和编译器选项的源文件。它是多平台项目中共享代码的主要方式。
经过gradle 插件创建的源集,可以直接使用 getting 获得
val commonMain by getting {
}源集之间依赖使用 dependsOn,例如
sourceSets { // Example of configuring the dependsOn relation iosArm64Main.dependsOn(commonMain) }中间源集
例如有时需要更精细的代码划分。需要让多个 iOS 目标共享相同的平台特定代码 如避免在 iosX64Main、iosArm64Main、iosSimulatorArm64Main 中重复写相同的依赖,就需要使用到中间源集。示例·:
//中间源集(自定义创建) val iosMain by creating { dependsOn(commonMain) dependencies { implementation("io.ktor:ktor-client-darwin:2.3.10") } } // 为所有 iOS 目标共享 iosMain listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { getByName("${it.targetName}Main") { dependsOn(iosMain) }此处的 iosMain 源集 是自定义的源集,不是有 gradle插件 创建的 注:手动创建的源集 只是作为 多目标代码共享,没有特定的编译目标。
| 对比维度 | gradle自动生成的源集 | 自定义中间源集 (手动 creating) |
|---|---|---|
| 命名规则 | 固定的约定命名,如 androidMain、iosArm64Main | 可以自由命名,如 iosMain、jvmAndJsMain 或 myDesktopMain |
| 创建方式 | 由插件根据你声明的 目标 targets 自动创建,使用 by getting 获取 | 需要你在 sourceSets 块中 手动创建,使用 by creating 声明 |
| 主要用途 | 存放特定平台(如Android、具体iOS设备架构)的代码 | 存放一组相关平台(如所有iOS目标、所有原生桌面目标)的共享代码,避免重复 |
| 核心价值 | 确保项目结构完整,为每个平台提供代码入口,有特定的编译目标 | 消除代码冗余,提高代码的组织性和可维护性,只是作为代码复用,无特定编译目标 |
完整示例
使用 Multiplatform Gradle DSL构建工具,参考配置语法
share 目录下的 build.dradle.kts 配置示例:
// 引入所需的 Gradle 插件plugins { kotlin("multiplatform") // Kotlin 多平台核心插件,用于多平台gradle构建 id("com.android.library") // Android 库插件,用于构建 Android 部分 kotlin("native.cocoapods") // CocoaPods 集成插件,用于 iOS 依赖管理 id("org.jetbrains.compose")// Compose Multiplatform 插件,支持跨平台 UI}
kotlin { // ==================== 目标平台配置 ====================
// Android 目标平台配置 androidTarget { compilations.all { kotlinOptions { jvmTarget = "17" // 设置 JVM 目标版本为 Java 17 } } }
// iOS 目标平台配置(三个目标:真机 x64、真机 arm64、模拟器 arm64) listOf( iosX64(), // 旧版 iOS 模拟器(Intel 芯片 Mac) iosArm64(), // 真实 iOS 设备(iPhone/iPad) iosSimulatorArm64() // 新版 iOS 模拟器(Apple Silicon 芯片 Mac) ).forEach { target -> target.binaries.framework { baseName = "Shared" // 生成的 Framework 名称 isStatic = true // 生成静态库(推荐,避免动态库签名问题)
// 导出依赖,使 Swift 代码可以直接访问 moko-resources export("dev.icerock.moko:resources:0.23.0") } }
// ==================== CocoaPods 集成配置 ==================== cocoapods { summary = "Kotlin Multiplatform Shared Module" // Pod 的简短描述 homepage = "https://github.com/your-org/project" // 项目主页 URL version = "1.0" // Pod 版本号 ios.deploymentTarget = "14.0" // iOS 最低支持版本
// 指向 iOS 项目的 Podfile 路径 podfile = project.file("../iosApp/Podfile")
framework { baseName = "shared" // CocoaPods 生成的 Framework 名称 isStatic = true // 使用静态库方式集成 }
// 依赖原生的 iOS Pod 库 pod("Alamofire") { version = "~> 5.8" // 版本约束:兼容 5.8.x } }
// ==================== 源码集依赖配置 ==================== sourceSets { // 通用代码(所有平台共享) val commonMain by getting { dependencies { // ----- 核心库 ----- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") // 协程支持 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") // JSON 序列化
// ----- 网络层(Ktor)----- implementation("io.ktor:ktor-client-core:2.3.10") // Ktor HTTP 客户端核心 implementation("io.ktor:ktor-client-content-negotiation:2.3.10") // 内容协商(JSON 处理) implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.10") // JSON 序列化集成
// ----- 依赖注入 ----- implementation("io.insert-koin:koin-core:3.5.6") // Koin 依赖注入框架
// ----- UI 资源管理 ----- implementation("dev.icerock.moko:resources:0.23.0") // 跨平台资源管理
// ----- Compose Multiplatform UI ----- implementation(compose.runtime) // Compose 运行时 implementation(compose.foundation) // Compose 基础组件 implementation(compose.material3) // Material 3 设计组件 } }
// 通用测试代码 val commonTest by getting { dependencies { // Kotlin 测试框架 implementation(kotlin("test")) // 协程测试支持 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") } }
// Android 平台特定代码 val androidMain by getting { dependencies { // Android 使用 OkHttp 引擎 implementation("io.ktor:ktor-client-okhttp:2.3.10") // Android 生命周期管理 implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") } }
// iOS 平台特定源码集 val iosMain by creating { dependsOn(commonMain) // 继承通用代码的依赖 dependencies { // iOS 使用 Darwin 原生引擎 implementation("io.ktor:ktor-client-darwin:2.3.10") } }
// 将所有 iOS 目标连接到 iosMain 源码集 listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { target -> getByName("${target.targetName}Main") { dependsOn(iosMain) // 让每个 iOS 目标的 Main 都依赖 iosMain } } }}
// ==================== Android 配置 ====================android { namespace = "com.gwt.cv.shared" // Android 命名空间 compileSdk = 34 // 编译 SDK 版本(Android 14)
defaultConfig { minSdk = 24 // 最低支持 Android 7.0 }
compileOptions { sourceCompatibility = JavaVersion.VERSION_17 // 源码兼容 Java 17 targetCompatibility = JavaVersion.VERSION_17 // 目标字节码 Java 17 }}预期与实际声明
预期声明和实际声明允许你访问Kotlin多平台模块中的平台特定API。你可以在通用代码中提供平台无关的API
未完待续
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时