Kotlin Multiplatform 教程:从零开始,拥抱跨平台开发的未来
在当今移动应用开发领域,覆盖多个平台(如 Android 和 iOS)是常态。然而,为每个平台维护独立的代码库不仅耗时耗力,还容易导致功能和行为的不一致。为了解决这一痛点,各种跨平台解决方案应运而生。Kotlin Multiplatform (KMP),作为 JetBrains 推出的官方解决方案,正以其独特的优势吸引着越来越多的开发者。
本教程将带你从零开始,一步步探索 Kotlin Multiplatform 的世界。我们将从基本概念讲起,搭建开发环境,创建你的第一个 KMP 项目,编写共享代码和平台特定代码,最后在 Android 和 iOS 平台上运行你的应用。无论你是经验丰富的 Kotlin 开发者,还是对跨平台开发充满好奇的新手,都能从中获益。
文章目录:
- 什么是 Kotlin Multiplatform (KMP)?
- 核心理念:代码共享的艺术
- KMP 与其他跨平台方案的对比
- KMP 的主要优势
- 准备工作:环境搭建
- 必备工具清单
- 安装与配置(JDK, IDE, Xcode, Android SDK)
- 验证环境
- 创建你的第一个 KMP 项目
- 使用 IntelliJ IDEA / Android Studio 向导
- 项目结构详解 (
shared
,androidApp
,iosApp
) - Gradle 构建系统简介
- 编写共享代码 (Common Code)
commonMain
的角色- 创建共享数据类和业务逻辑
- 标准库与协程的使用
- 处理平台差异:
expect
/actual
- 为何需要平台特定代码?
expect
关键字:定义期望actual
关键字:提供实现- 实战演练:获取平台名称
- 在 Android 平台运行
androidApp
模块与shared
模块的依赖关系- 在 Android Activity/ViewModel 中调用共享代码
- 构建与运行 Android 应用
- 在 iOS 平台运行
- Kotlin/Native 与 Framework 的生成
- 在 Xcode 中配置与链接
- 在 Swift/Objective-C 中调用共享代码
- 构建与运行 iOS 应用
- UI 层:Compose Multiplatform 简介
- 超越逻辑共享:UI 的可能性
- Compose Multiplatform 的概念与优势
- 现状与未来
- 依赖管理
- 添加通用库 (Ktor, Serialization 等)
- 添加平台特定库
- Gradle 配置示例
- 测试
- 共享代码测试 (
commonTest
) - 平台特定代码测试 (
androidTest
,iosTest
) - 测试框架简介
- 共享代码测试 (
- 总结与展望
- 回顾 KMP 的核心价值
- KMP 生态系统的发展
- 下一步学习建议
1. 什么是 Kotlin Multiplatform (KMP)?
Kotlin Multiplatform (KMP) 是 JetBrains 开发的一种技术,允许开发者在不同平台(如 Android, iOS, JVM 服务器, Web (JavaScript), macOS, Windows, Linux 等)之间共享 Kotlin 代码。
核心理念:代码共享的艺术
KMP 的核心思想不是“一次编写,到处运行”(Write Once, Run Anywhere),而是“一次编写,随处编译”(Write Once, Compile Anywhere)。它专注于共享那些与平台无关的业务逻辑、数据层和领域模型,同时允许你为每个目标平台编写原生的 UI 和平台特定功能。
这意味着你可以:
- 共享核心逻辑: 网络请求、数据解析、数据库操作、状态管理、算法、数据验证等。
- 保持原生体验: UI 部分仍然可以使用各自平台的原生技术栈(Android 的 XML/Jetpack Compose, iOS 的 UIKit/SwiftUI)或者使用新兴的 Compose Multiplatform 实现共享 UI。平台相关的 API 调用(如 GPS、相机、蓝牙)也可以通过特定机制实现。
KMP 与其他跨平台方案的对比
- React Native / NativeScript: 主要使用 JavaScript/TypeScript,通过桥接或直接调用原生 API 来实现跨平台。UI 也是用其特定方式描述,然后渲染为原生组件。KMP 更侧重逻辑共享,UI 默认原生。
- Flutter: 使用 Dart 语言,拥有自己的渲染引擎(Skia),可以绘制几乎完全一致的 UI 到不同平台。它提供了完整的框架,但学习曲线相对陡峭,且与原生平台的集成有时需要额外工作。KMP 更灵活,可以选择性共享,与原生集成更自然。
- Xamarin: 使用 C# 和 .NET,与 KMP 类似,可以共享逻辑,并提供 Xamarin.Forms 用于共享 UI。生态系统与 .NET 紧密绑定。
KMP 的主要优势
- 代码复用,降本增效: 显著减少为不同平台编写和维护重复逻辑代码的工作量。
- 行为一致性: 核心业务逻辑来自同一份代码,确保了不同平台应用行为的一致性。
- 原生性能与体验: 由于 UI 和平台特定功能可以(或默认)使用原生代码,应用可以达到最佳性能和用户体验。Kotlin/Native 将 Kotlin 代码直接编译成目标平台的原生二进制文件(iOS 为 LLVM),避免了桥接带来的性能损耗。
- 灵活的采用策略: 你可以逐步将 KMP 集成到现有项目中,不必完全重写。可以先从共享一小部分逻辑开始。
- 强大的 Kotlin 语言特性: 享受 Kotlin 带来的空安全、协程、扩展函数、DSL 等现代语言特性。
- 活跃的生态系统: JetBrains 官方支持,社区活跃,越来越多的库支持 KMP。
2. 准备工作:环境搭建
要开始 KMP 开发,你需要安装以下工具:
必备工具清单
- JDK (Java Development Kit): Kotlin 运行在 JVM 上(对于 Android 和 JVM 后端),构建工具也需要它。推荐使用 JDK 11 或更高版本。
- IDE (Integrated Development Environment):
- IntelliJ IDEA (Community 或 Ultimate): JetBrains 官方 IDE,对 Kotlin 支持最佳。推荐使用最新版本。
- Android Studio: 基于 IntelliJ IDEA,专门用于 Android 开发,也内置了 KMP(尤其是 KMM – Kotlin Multiplatform Mobile)项目模板和插件。推荐使用最新稳定版 (e.g., Giraffe, Hedgehog 或更高)。
- Kotlin Plugin: 通常 IntelliJ IDEA 和 Android Studio 会自带或提示安装最新的 Kotlin 插件。确保它是启用的。
- Xcode: (仅限 macOS) 如果你需要开发和运行 iOS 应用,必须安装 Xcode。可以从 Mac App Store 下载。Xcode 包含了 iOS SDK、模拟器以及编译 Swift/Objective-C 代码所需的工具链。
- Android SDK: 通常通过 Android Studio 的 SDK Manager 安装和管理。它包含了构建和运行 Android 应用所需的库和工具。
- (可选) CocoaPods: (仅限 macOS) 如果你的 KMP 项目以 CocoaPods 依赖项的形式集成到现有 iOS 项目中,可能需要安装 CocoaPods。可以通过 RubyGems 安装 (
sudo gem install cocoapods
)。不过,新的 KMP 项目模板通常使用更现代的框架集成方式。
安装与配置
- JDK: 访问 Oracle JDK 或 OpenJDK (如 Adoptium Temurin) 的官网下载并安装。设置
JAVA_HOME
环境变量通常是个好主意。 - IDE: 从 JetBrains 官网下载 IntelliJ IDEA 或从 Google 开发者官网下载 Android Studio,按提示安装。
- Xcode: 在 Mac App Store 搜索 Xcode 并安装。首次运行可能需要同意许可协议并安装附加组件。
- Android SDK: 打开 Android Studio,进入
Settings/Preferences
->Appearance & Behavior
->System Settings
->Android SDK
。确保至少安装了一个平台的 SDK (e.g., Android 13.0 – Tiramisu) 和必要的Android SDK Build-Tools
,Android Emulator
,Android SDK Platform-Tools
。
验证环境
- 打开终端或命令提示符:
- 输入
java -version
确认 JDK 安装成功并查看版本。 - (macOS) 输入
xcode-select -p
确认 Xcode 命令行工具已设置。
- 输入
- 打开 IntelliJ IDEA / Android Studio,检查 Kotlin 插件是否已安装并启用 (
Settings/Preferences
->Plugins
->Installed
)。 - 在 Android Studio 中,尝试创建一个空的 Android 项目并运行到模拟器或设备上,确保 Android 开发环境正常。
- (macOS) 打开 Xcode,尝试创建一个空的 iOS 项目并运行到模拟器上,确保 iOS 开发环境正常。
3. 创建你的第一个 KMP 项目
现在环境准备就绪,让我们创建第一个 KMP 项目。我们将使用 Android Studio (或 IntelliJ IDEA 配合 KMM 插件) 提供的模板。
使用 IntelliJ IDEA / Android Studio 向导
- 打开 Android Studio (Hedgehog 或更高版本推荐,因为它内置了 KMM 模板)。
- 选择 “New Project”。
- 在左侧模板列表中,找到并选择 “Kotlin Multiplatform”。
- 在右侧选择 “Kotlin Multiplatform App” 模板。点击 “Next”。
- 配置你的项目:
- Name: 应用的名称 (e.g.,
MyKMPApp
)。 - Package name: 通常是反向域名格式 (e.g.,
com.example.mykmpapp
)。 - Save location: 项目存储路径。
- Minimum SDK: 选择你的 Android 应用支持的最低 API 级别。
- iOS framework distribution: 选择如何将共享代码提供给 iOS。推荐使用 “Regular framework”。
- Add sample tests for Shared Module: 勾选此项,会生成一些测试示例。
- Name: 应用的名称 (e.g.,
- 点击 “Finish”。IDE 会下载所需的依赖并设置项目结构。这可能需要一些时间。
项目结构详解
KMP 项目通常采用分层模块结构:
MyKMPApp/
├── .gradle/
├── .idea/
├── androidApp/ # Android 应用模块
│ ├── src/main/
│ │ ├── java/com/example/mykmpapp/android/ # Android 特定代码 (Activity, etc.)
│ │ └── res/ # Android 资源文件
│ └── build.gradle.kts # Android 模块构建脚本
├── iosApp/ # iOS 应用文件夹 (Xcode 项目)
│ ├── iosApp.xcodeproj/
│ ├── iosApp/
│ │ ├── ContentView.swift # iOS UI 代码 (SwiftUI)
│ │ └── ...
│ └── Shared/ # 指向共享模块编译出的 Framework
├── shared/ # 共享代码模块 (核心)
│ ├── src/
│ │ ├── androidMain/kotlin/ # Android 平台的 'actual' 实现
│ │ ├── commonMain/kotlin/ # 平台无关的共享代码 ('expect' 声明)
│ │ ├── iosMain/kotlin/ # iOS 平台的 'actual' 实现
│ │ ├── androidUnitTest/kotlin/ # Android 平台单元测试
│ │ ├── commonTest/kotlin/ # 共享代码单元测试
│ │ └── iosTest/kotlin/ # iOS 平台单元测试
│ └── build.gradle.kts # 共享模块构建脚本
├── build.gradle.kts # 项目根构建脚本
└── settings.gradle.kts # 项目设置脚本 (包含模块)
shared
模块: 这是 KMP 的核心。commonMain
: 存放所有平台共享的 Kotlin 代码。这里的代码不能直接依赖任何特定平台的 API。androidMain
: 存放仅用于 Android 平台的 Kotlin 代码。这里可以访问 Android SDK API,并且需要为commonMain
中的expect
声明提供actual
实现。iosMain
: 存放仅用于 iOS 平台的 Kotlin 代码。这里可以访问 iOS/macOS 的 Foundation, UIKit 等框架 (通过 Kotlin/Native 的互操作性),并为commonMain
中的expect
声明提供actual
实现。commonTest
,androidTest
,iosTest
: 分别对应上述三个源集的测试代码。
androidApp
模块: 一个标准的 Android 应用模块。它依赖于shared
模块,可以像调用本地库一样调用shared
模块中的公共代码。iosApp
文件夹: 包含一个 Xcode 项目。这个项目配置为链接并使用shared
模块编译生成的 iOS Framework。
Gradle 构建系统简介
Gradle 是 KMP 项目的构建工具。它负责:
- 管理项目依赖(下载库)。
- 编译 Kotlin 代码(针对 JVM、Native、JS 等)。
- 为不同平台打包(生成 .apk, .ipa (间接通过 Xcode), .jar 等)。
- 运行测试。
项目中有多个 build.gradle.kts
文件(使用 Kotlin Script, .kts
后缀):
- 根目录
build.gradle.kts
: 定义全局配置,如插件版本。 settings.gradle.kts
: 声明项目中包含哪些模块 (androidApp
,shared
)。shared/build.gradle.kts
: 配置shared
模块,这是 KMP 配置的核心。这里会定义目标平台(android, iosX64, iosArm64, iosSimulatorArm64 等)、源集(commonMain
,androidMain
,iosMain
)以及它们的依赖关系。androidApp/build.gradle.kts
: 标准的 Android 应用构建脚本,但会添加对shared
模块的项目依赖implementation(project(":shared"))
。
4. 编写共享代码 (Common Code)
共享代码是 KMP 的核心价值所在。这部分代码位于 shared/src/commonMain/kotlin
目录下。
commonMain
的角色
这里的代码是平台无关的。你可以使用:
- Kotlin 标准库 (
kotlin-stdlib-common
)。 - 支持 KMP 的通用库(如
kotlinx.coroutines
,kotlinx.serialization
,Ktor
,SQLDelight
等)。 - 你自己编写的纯 Kotlin 逻辑。
创建共享数据类和业务逻辑
让我们创建一个简单的例子:一个打招呼的功能。
- 在
shared/src/commonMain/kotlin
下创建一个包(e.g.,com.example.mykmpapp.shared
)。 - 在该包下创建一个 Kotlin 文件,例如
Greeting.kt
。 - 在
Greeting.kt
中添加以下代码:
“`kotlin
package com.example.mykmpapp.shared
class Greeting {
private val platform = getPlatform() // 调用将在后面定义的 expect 函数
fun greet(): String {
return "你好, ${platform.name}! 这是来自共享代码的问候。"
// 注意:我们暂时还未定义 getPlatform(),下面会讲到
// 为了编译通过,可以暂时注释掉 platform 相关代码或先定义 expect/actual
}
}
// 暂时注释掉上面 platform 相关行,或者先完成下面的 expect/actual 定义
// 平台信息接口,将在 expect/actual 中实现
// interface Platform {
// val name: String
// }
// expect fun getPlatform(): Platform // 在 commonMain 定义期望
“`
这个 Greeting
类包含一个简单的 greet
方法,它将用于生成一个问候字符串。我们还预留了一个获取平台信息的调用,这将通过 expect
/actual
实现。
标准库与协程的使用
commonMain
可以直接使用 Kotlin 标准库中的大部分功能。对于异步操作,kotlinx.coroutines
库也提供了 common
版本,允许你在共享代码中编写挂起函数(suspend fun
)和使用 Flow。
例如,模拟一个异步获取数据的函数:
“`kotlin
package com.example.mykmpapp.shared
import kotlinx.coroutines.delay
class DataRepository {
suspend fun fetchData(): String {
delay(1000) // 模拟网络延迟
return “从共享代码异步获取的数据”
}
}
“`
5. 处理平台差异:expect
/ actual
当共享代码需要访问特定平台的 API 或功能时(例如获取设备信息、文件系统访问、调用原生 UI 组件等),KMP 提供了 expect
和 actual
关键字机制。
expect
: 在commonMain
中声明。它像一个接口或抽象类/方法/属性的声明,定义了期望的功能或类型,但不提供具体实现。actual
: 在对应的平台源集(androidMain
,iosMain
等)中提供。它实现了commonMain
中expect
声明的功能。
为何需要平台特定代码?
不同的操作系统提供了不同的 API 来实现相同的功能。例如,获取设备型号的方式在 Android 和 iOS 上是完全不同的。expect
/actual
允许你在 commonMain
中定义一个统一的接口,然后在各自的平台模块中提供具体的原生实现。
实战演练:获取平台名称
让我们完成之前 Greeting
类中获取平台名称的功能。
-
在
commonMain
中定义expect
:
打开或创建Platform.kt
文件 (或放在Greeting.kt
文件里,但最好分开) 在shared/src/commonMain/kotlin/com/example/mykmpapp/shared
目录下:“`kotlin
package com.example.mykmpapp.shared// 定义期望的接口/类
expect class Platform() {
val name: String
}// 定义期望的顶层函数
// 或者可以直接 expect 一个函数返回 String
// expect fun getPlatformName(): String
``
expect class
*注意:或
expect fun都可以。这里用
expect class` 演示。* -
在
androidMain
中提供actual
实现:
在shared/src/androidMain/kotlin/com/example/mykmpapp/shared
目录下创建Platform.kt
文件:“`kotlin
package com.example.mykmpapp.sharedimport android.os.Build
// 提供 Android 平台的实际实现
actual class Platform actual constructor() {
actual val name: String = “Android ${Build.VERSION.RELEASE}”
}
``
android.os.Build` 来获取 Android 版本号。
这里我们导入了 -
在
iosMain
中提供actual
实现:
在shared/src/iosMain/kotlin/com/example/mykmpapp/shared
目录下创建Platform.kt
文件:“`kotlin
package com.example.mykmpapp.sharedimport platform.UIKit.UIDevice // 导入 iOS 框架
// 提供 iOS 平台的实际实现
actual class Platform actual constructor() {
actual val name: String = UIDevice.currentDevice.systemName() + ” ” + UIDevice.currentDevice.systemVersion
}
``
platform.UIKit.UIDevice` 来获取 iOS 系统名称和版本。
这里我们通过 Kotlin/Native 的互操作性导入并使用了 -
在
commonMain
中使用:
现在,回到commonMain
的Greeting.kt
文件,你可以安全地使用Platform()
了:“`kotlin
package com.example.mykmpapp.sharedclass Greeting {
private val platform = Platform() // 现在可以实例化 Platformfun greet(): String { return "你好, ${platform.name}! 这是来自共享代码的问候。" }
}
``
androidMain
编译器知道,在编译 Android 目标时,会链接中的
actual实现;在编译 iOS 目标时,会链接
iosMain中的
actual` 实现。
6. 在 Android 平台运行
现在共享代码已经准备好,我们可以在 Android 应用中调用它了。
androidApp
模块与 shared
模块的依赖关系
检查 androidApp/build.gradle.kts
文件,你会看到类似这样的依赖声明:
“`kotlin
android {
// … standard Android config …
}
dependencies {
implementation(project(“:shared”)) // 关键:依赖 shared 模块
// … other Android dependencies …
}
``
androidApp
这使得可以访问
shared模块中
commonMain和
androidMain里的
public或
internal` (如果它们在同一个 Gradle 模块内) 的类和函数。
在 Android Activity/ViewModel 中调用共享代码
- 打开
androidApp/src/main/java/com/example/mykmpapp/android/MainActivity.kt
(路径可能略有不同)。 -
修改
onCreate
方法来调用我们的Greeting
类:“`kotlin
package com.example.mykmpapp.androidimport android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.example.mykmpapp.shared.Greeting // 导入共享模块的类class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(
modifier = androidx.compose.ui.Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
// 调用共享代码并显示结果
GreetingView(Greeting().greet())
}
}
}
}
}@Composable
fun GreetingView(text: String) {
Text(text = text)
}@Preview
@Composable
fun DefaultPreview() {
MyApplicationTheme {
GreetingView(“Hello, Android!”)
}
}
``
MainActivity
*(注意:模板生成的可能使用 Jetpack Compose。以上代码适配了这种情况。如果是传统 View 系统,则在
TextView` 中设置文本即可。)*
构建与运行 Android 应用
- 在 Android Studio 顶部的配置下拉菜单中,确保选中
androidApp
。 - 选择一个可用的 Android 模拟器或连接一个物理设备。
- 点击绿色的 “Run” (▶️) 按钮。
Gradle 会编译 shared
模块 (针对 Android JVM) 和 androidApp
模块,然后将应用安装并运行在目标设备/模拟器上。你应该能看到类似 “你好, Android X.X! 这是来自共享代码的问候。” 的文本。
7. 在 iOS 平台运行
要在 iOS 上运行,过程略有不同,因为涉及到 Kotlin/Native 编译和与 Xcode 的集成。
Kotlin/Native 与 Framework 的生成
当你构建 iOS 目标时,shared
模块的 Kotlin 代码 (包括 commonMain
和 iosMain
) 会被 Kotlin/Native 编译器编译成一个原生的 iOS Framework。这个 Framework 包含了可以在 Swift 或 Objective-C 中调用的代码。
在 Xcode 中配置与链接
KMP 项目模板通常已经为你配置好了 Xcode 项目 (iosApp/iosApp.xcodeproj
)。
- 在 Android Studio 的项目视图中找到
iosApp
文件夹。 - 右键点击
iosApp.xcodeproj
文件,选择 “Open in Xcode” (如果选项可用) 或者直接在 Finder 中双击打开。 - Xcode 会打开
iosApp
项目。 - 在 Xcode 中,选择一个 iOS 模拟器(例如 iPhone 15 Pro)或者连接一个物理设备(可能需要配置开发者账号和签名)。
- 查看 Xcode 项目的配置:
- 在
Build Settings
中搜索 “Framework Search Paths”,应该能看到指向shared/build/XCFramewoks/debug
或release
目录的路径。 - 在
Build Phases
->Link Binary With Libraries
中,应该能看到Shared.framework
(或类似名称,取决于shared/build.gradle.kts
中的配置)。
- 在
在 Swift/Objective-C 中调用共享代码
- 打开
iosApp/iosApp/ContentView.swift
(如果使用 SwiftUI 模板) 或ViewController.swift
(如果使用 UIKit)。 -
导入共享模块。模块名称通常是你在
shared/build.gradle.kts
中定义的framework
名称(默认为Shared
)。“`swift
import SwiftUI
import Shared // 导入共享框架struct ContentView: View {
// 调用共享代码
let greet = Greeting().greet() // 注意:类和方法名可能与 Kotlin 中完全一致var body: some View { Text(greet) // 在 SwiftUI 视图中显示结果 }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
``
internal` 可见性的 Kotlin 代码在 Swift 中不可见。*
*注意:Kotlin 类和方法在 Swift 中的映射通常很直接。
构建与运行 iOS 应用
- 在 Xcode 中,确保顶部选择了正确的 Target (
iosApp
) 和目标设备/模拟器。 - 点击 Xcode 的 “Run” (▶️) 按钮。
Xcode 会触发 Gradle 构建(如果需要,它会编译 Kotlin/Native 生成 Framework),然后编译 Swift 代码,最后将应用安装并运行在选定的模拟器或设备上。你应该能看到类似 “你好, iOS XX.X! 这是来自共享代码的问候。” 的文本。
8. UI 层:Compose Multiplatform 简介**
到目前为止,我们的教程主要集中在共享业务逻辑。UI 部分,Android 使用了 Jetpack Compose (或 XML),iOS 使用了 SwiftUI (或 UIKit)。这是 KMP 的经典用法,保证了最佳的原生 UI 体验。
然而,JetBrains 正在大力发展 Compose Multiplatform。
超越逻辑共享:UI 的可能性
Compose Multiplatform 是 Jetpack Compose 的一个扩展,旨在将 Google 现代化的声明式 UI 框架带到更多平台。
Compose Multiplatform 的概念与优势
- 共享 UI 代码: 使用相同的 Kotlin 代码和 Compose API 来为 Android, iOS (Alpha/Beta 阶段), Desktop (Windows, macOS, Linux), 和 Web (Wasm – Experimental) 构建 UI。
- 声明式 UI: 与 Jetpack Compose 和 SwiftUI 类似,你描述你的 UI 状态,框架负责更新。
- 提升开发效率: 如果 UI 在各平台非常相似,共享 UI 可以极大地减少开发和维护成本。
- 与 KMP 完美结合: 自然地与共享的业务逻辑层(ViewModel/Presenter)集成。
现状与未来
- Android & Desktop: 非常稳定和成熟。
- iOS: 目前处于 Beta 阶段。这意味着 API 可能会有变化,性能和稳定性仍在持续改进中,但已经可以用于生产应用(需要谨慎评估)。它通过在 iOS 画布上绘制 UI(类似 Flutter)来实现。
- Web (Wasm): 处于 Experimental 阶段,未来潜力巨大,但目前尚不建议用于复杂生产环境。
如果你想尝试共享 UI,可以在创建 KMP 项目时选择包含 Compose Multiplatform 的模板,或者手动将其添加到现有项目中。这需要额外的配置和对 Compose 框架的学习。
9. 依赖管理
KMP 项目使用 Gradle 来管理依赖。你可以在 shared/build.gradle.kts
中为不同的源集添加库。
添加通用库 (Ktor, Serialization 等)
对于需要在所有平台使用的库(必须是支持 KMP 的库),在 commonMain
的依赖块中添加:
“`kotlin
// In shared/build.gradle.kts
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
// Coroutines
implementation(“org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3”) // Use the latest version
// Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
// Ktor (HTTP Client) - Core
implementation("io.ktor:ktor-client-core:2.3.5")
// Ktor Serialization
implementation("io.ktor:ktor-client-content-negotiation:2.3.5")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.5")
// SQLDelight (Database) - Common driver
// implementation("app.cash.sqldelight:runtime:2.0.0")
}
}
// ... other source sets ...
}
}
“`
添加平台特定库
- 特定引擎/驱动: 像 Ktor 需要平台特定的 HTTP 引擎,SQLDelight 需要平台特定的数据库驱动。
- 平台 API 封装库: 有些库可能只封装了某个平台的特定功能。
这些依赖项添加到对应的平台源集(androidMain
, iosMain
)的依赖块中:
“`kotlin
// In shared/build.gradle.kts
kotlin {
androidTarget { // Or android() in older plugin versions
// …
}
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
val commonMain by getting {
// ... common dependencies ...
}
val androidMain by getting {
dependencies {
// Ktor Android engine
implementation("io.ktor:ktor-client-okhttp:2.3.5")
// SQLDelight Android driver
// implementation("app.cash.sqldelight:android-driver:2.0.0")
// Android specific libraries (e.g., androidx) can be used here if needed for 'actual' implementations
implementation("androidx.core:core-ktx:1.9.0")
}
}
val iosMain by creating { // Note: iosMain might be implicitly created or need creating depending on template/setup
dependsOn(commonMain) // Ensure iosMain depends on commonMain
// Link iosX64, iosArm64, iosSimulatorArm64 source sets to iosMain if needed
// or add dependencies directly to iosX64Main, iosArm64Main etc. if implementations differ
dependencies {
// Ktor iOS engine (Darwin)
implementation("io.ktor:ktor-client-darwin:2.3.5")
// SQLDelight iOS driver (Native)
// implementation("app.cash.sqldelight:native-driver:2.0.0")
}
}
// If implementations differ significantly between simulator and device:
// val iosX64Main by getting { ... }
// val iosArm64Main by getting { ... }
// val iosSimulatorArm64Main by getting { ... }
}
}
``
iosMain
*注意:的配置可能因 KMP Gradle 插件版本和项目设置而略有不同。有时会直接配置
iosX64Main,
iosArm64Main` 等。*
添加依赖后,点击 IDE 提示的 “Sync Now” 或手动同步 Gradle 项目。
10. 测试**
测试是保证共享代码质量的关键。KMP 项目模板通常已经设置好了测试结构。
shared/src/commonTest/kotlin
: 用于测试commonMain
中的代码。这里的测试代码也是平台无关的。可以使用kotlin.test
库提供的断言 (assertEquals
,assertTrue
等)。shared/src/androidTest/kotlin
: 用于测试 Android 相关的代码 (androidMain
) 或在 Android 环境下测试commonMain
的代码。可以使用 JUnit 4/5 和 AndroidX 测试库(如 Robolectric, Espresso)。shared/src/iosTest/kotlin
: 用于测试 iOS 相关的代码 (iosMain
) 或在 iOS 环境下测试commonMain
的代码。可以使用kotlin.test
运行在 iOS 模拟器或设备上,也可以与 Xcode 的 XCTest 集成。
共享代码测试 (commonTest
)
在 commonTest
中创建一个测试类:
“`kotlin
package com.example.mykmpapp.shared
import kotlin.test.Test
import kotlin.test.assertTrue
class CommonGreetingTest {
@Test
fun testExample() {
// 调用 commonMain 中的代码进行测试
// 注意:如果 Greeting 依赖 expect Platform,测试可能需要模拟或在特定平台运行
//assertTrue(Greeting().greet().contains("你好"), "Check '你好' is mentioned")
// 更简单的测试,不依赖 expect/actual
val simpleLogic = SimpleLogic()
assertTrue(simpleLogic.add(2, 2) == 4, "Check addition")
}
}
// 一个简单的无平台依赖的类用于测试
class SimpleLogic {
fun add(a: Int, b: Int) = a + b
}
“`
平台特定代码测试
在 androidTest
或 iosTest
中,你可以:
- 测试
actual
实现的正确性。 - 测试
commonMain
中的代码在特定平台环境下的行为(例如,测试使用了expect
/actual
的类)。
例如,在 androidTest
中测试 actual Platform
:
“`kotlin
package com.example.mykmpapp.shared // Same package as actual
import org.junit.Test
import org.junit.Assert.assertTrue
class AndroidPlatformTest {
@Test
fun testPlatformName() {
val platform = Platform() // Instantiates the actual Android Platform
assertTrue(platform.name.startsWith(“Android”))
}
}
“`
你可以通过 IDE 的 Gutter 图标 (测试类或方法旁的绿色箭头) 或 Gradle 任务 (./gradlew check
) 来运行测试。
11. 总结与展望
恭喜你!你已经完成了 Kotlin Multiplatform 的入门之旅。我们一起探索了:
- KMP 的核心概念、优势以及与其他方案的区别。
- 如何搭建 KMP 开发环境。
- 创建了一个基本的 KMP 项目,并理解了其结构。
- 如何在
commonMain
中编写共享的业务逻辑和数据模型。 - 使用
expect
/actual
机制处理平台差异。 - 如何在 Android 和 iOS 应用中调用共享代码并运行。
- 简要了解了 Compose Multiplatform 用于共享 UI 的可能性。
- 如何在 KMP 项目中管理依赖和编写测试。
回顾 KMP 的核心价值
KMP 提供了一种务实且灵活的跨平台开发方式。它允许你最大限度地重用非 UI 代码,同时保留使用原生 UI 和平台 API 的能力,从而在开发效率和原生体验之间取得良好平衡。
KMP 生态系统的发展
KMP 不再是一个实验性项目。它已经足够稳定,被许多公司(包括 JetBrains 自己的产品、Netflix、VMware、Philips 等)应用于生产环境。
- 工具链成熟: Kotlin 编译器、Gradle 插件、IDE 支持都在不断完善。
- 库支持广泛: 越来越多的流行库(网络、数据库、序列化、依赖注入、状态管理等)提供了 KMP 版本。
- 社区活跃: KMP 开发者社区日益壮大,提供了丰富的资源和支持。
- Compose Multiplatform for iOS 进入 Beta: 这是 KMP 生态的一个重要里程碑,为 UI 共享提供了官方支持的强大选项。
下一步学习建议
- 官方文档: JetBrains 的 Kotlin Multiplatform 官方文档是最新、最权威的信息来源 (https://kotlinlang.org/docs/multiplatform-get-started.html)。
- 示例项目: 浏览 GitHub 上的 KMP 示例项目,了解更复杂的应用场景和最佳实践。
- 深入特定库: 学习如何在 KMP 中使用 Ktor (网络), SQLDelight (数据库), kotlinx.serialization (JSON), kotlinx.coroutines (异步) 等核心库。
- 探索 Compose Multiplatform: 如果你对共享 UI 感兴趣,深入学习 Compose Multiplatform,特别是其 iOS 的支持情况和使用方式。
- 架构模式: 研究如何在 KMP 项目中应用常见的架构模式,如 MVVM, MVI, Clean Architecture 等,来组织你的共享代码和平台代码。
- 加入社区: 参与 Kotlin Slack (#multiplatform 频道), Reddit (r/Kotlin), 或其他开发者论坛,与其他 KMP 开发者交流。
Kotlin Multiplatform 是一个激动人心的技术,它为应对现代应用开发的多平台挑战提供了一个强大而优雅的解决方案。希望本教程为你打开了 KMP 的大门,祝你在跨平台开发的道路上探索愉快!