1. 开始之前
前提条件
- 了解如何构建 Jetpack Compose 应用。
- 有 Kotlin 使用经验。
- 基本了解 Swift 语法。
您需要准备什么
- 最新稳定版 Android Studio(Meerkat 或更高版本)。
- 安装了 Xcode 16.1 和 iPhone 模拟器(iOS 16.0 或更高版本)的 macOS 系统。
您将学到什么
- 了解 Kotlin Multiplatform 的基础知识。
- 如何在平台之间共享代码。
- 如何在 Android 和 iOS 上连接共享代码。
2. 设置
要开始使用,请按照以下步骤操作
- 克隆 GitHub 代码库
$ git clone https://github.com/android/codelab-android-kmp.git
或者,您可以将代码库下载为 ZIP 文件
- 在 Android Studio 中,打开
get-started
项目,其中包含以下分支
main
:包含此项目的起始代码,您将在此处进行更改以完成 Codelab。end
:包含此 Codelab 的解决方案代码。
此 Codelab 从 main
分支开始。您可以按照自己的节奏逐步完成 Codelab。
- 如果您想查看解决方案代码,请运行以下命令
$ git clone -b end https://github.com/android/codelab-android-kmp.git
或者,您可以下载解决方案代码
安装 Xcode
要构建并运行此 Codelab 的 iOS 部分,您需要 Xcode 和一个 iOS 模拟器
- 从 Mac App Store 安装 Xcode(为此您需要一个 Apple 帐号)。
- 安装完成后,启动 Xcode。
- 将显示一个对话框,指示哪些组件是内置的,哪些需要您下载。
- 勾选 iOS 18.4(或更新版本)。
- 点击下载并安装。
- 等待组件安装完成。
此 Codelab 已使用 Xcode 16.3 进行测试。如果您使用任何其他 Xcode 版本并遇到问题,我们建议您下载此 Codelab 中提及的 exact 版本。
示例应用
此代码库包含使用 Jetpack Compose 构建的 Android 应用和使用 SwiftUI 构建的 iOS 应用。Android 项目位于 androidApp/
文件夹中,而 iOS 项目位于 iosApp/
文件夹中,其中还包含要使用 Xcode 运行的 KMPGetStartedCodelab.xcodeproj
文件。
3. Kotlin Multiplatform 简介
Kotlin Multiplatform (KMP) 允许您编写一次代码,并在多个目标平台(如 Android、iOS、Web 和桌面端)之间共享。通过利用 KMP,您可以最大程度地减少代码重复,保持一致性,并显著减少开发时间和精力。
KMP 不会强制规定您需要共享代码库的多少部分或哪些部分。您可以自行决定代码的哪些部分值得共享。
决定共享哪些内容
这些共享代码使您能够在跨平台时保持一致性并减少重复。许多移动团队首先共享一组离散的业务逻辑(例如,数据库访问、网络访问等)及其相关的测试,然后随着时间的推移共享额外的代码。
许多 Android Jetpack 库支持 KMP。已实现多平台兼容的 Jetpack 库根据目标平台提供多个级别的支持。有关库及其支持级别的完整列表,请参阅文档。
例如,其中一个支持的库是 Room 数据库库,它支持 Android、iOS 和桌面端。这允许您将数据库创建和相关的数据库逻辑移植到通用的 KMP 共享模块中,同时保留两个平台上的其他原生代码。
迁移数据库后可能的下一步是共享其他域逻辑。然后,您还可以考虑使用 Android Jetpack 中的 ViewModel
多平台库。
如何编写平台特定代码
Kotlin Multiplatform 引入了实现平台特定功能的新技术。
Expect 和 actual 声明
expect
actual
Kotlin 语言特性旨在支持 Kotlin 多平台代码库并提供完整的 IDE 支持。
当平台特定行为可以封装在单个函数或类中时,此方法是理想选择。它是一种灵活而强大的机制。例如,一个通用的 expect
类可以有平台特定的 actual
对应项,具有更开放的可访问性修饰符、额外的超类型,或不同的参数类型或修饰符。这些变体类型严格的接口 API 是无法实现的。此外,expect
actual
是静态解析的,这意味着平台特定的实现会在编译时强制执行。
Kotlin 中的接口和实现
如果两个平台需要遵循相似的 API 但实现不同,您可以在共享代码中定义接口,作为 expect 和 actual 声明的替代方案。此方法使您可以使用不同的测试实现,或在运行时切换到不同的实现。
此外,接口不需要特定的 Kotlin 知识,这使得熟悉其他语言中接口的开发者更容易上手此选项。
通用共享代码中的接口,原生代码(Android 或 Swift)中的实现。
在某些情况下,您需要编写 KMP 代码中不可用的代码。在这种情况下,您可以在共享代码中定义接口,在 Kotlin 中实现 Android 部分,并在 Swift 中提供 iOS 对应部分。通常,原生实现然后通过依赖注入或直接注入到共享代码中。此策略允许在每个平台上获得定制体验,同时为共享代码维护一个通用接口。
4. 从 Android Studio 打开 Xcode 项目
安装 Xcode 后,您应确保可以运行 iOS 应用。
您可以直接从 Android Studio 打开 iOS 项目。
- 切换项目窗格以使用 Project View
- 在 [root]/iosApp/ 文件夹中找到 KmpGetStartedCodelab.xcodeproj 文件
- 右键点击文件,然后选择 Open In 和 Open in Associated Application。这将使用 Xcode 打开 iOS 应用。
- 通过点击 ⌘+R 或导航到 Product 菜单并选择 Run,在 Xcode 中运行项目
.
5. 添加 KMP 模块
要向您的项目添加 KMP 支持,首先创建一个 shared
模块,用于在平台之间(Android、iOS)重用的代码。
Android Studio 提供了一种使用其 KMP Shared module 模板添加 Kotlin Multiplatform 模块的方法。
要在 Android Studio 中创建 KMP 模块,请执行以下操作
- 依次导航到 File > New > New Module > Kotlin Multiplatform Shared Module
- 将软件包更改为
com.example.kmp.shared
- 点击 Finish
- 模块创建完成且 Gradle 同步完成后,项目中将出现一个新的
shared
模块。要查看如下所示的视图,您可能需要从 Android View 切换到 Project View。
KMP Shared module 模板生成的共享模块包含一些基本的占位函数和测试。这些占位符确保模块从一开始就编译和运行成功。
重要提示:请记住 iosApp 文件夹和 iosMain 文件夹之间的区别。iosApp 文件夹包含独立的 iOS 应用代码,而 iosMain 是您刚刚添加的 KMP 共享模块的一部分。iosApp 包含 Swift 代码,而 iosMain 包含 iOS 平台特定的 KMP 代码。
将共享模块链接到 Android 应用
首先,您需要将新的共享模块链接为 :androidApp
Gradle 模块中的依赖项,以使应用能够使用共享代码
- 打开
androidApp/build.gradle.kts
文件 - 在 dependencies 块中添加
shared
模块依赖项,如下所示
dependencies {
...
implementation(projects.shared)
}
- 将项目与 Gradle 文件同步
验证代码对 shared
模块的访问
为了验证 Android 应用可以访问 shared
模块中的代码,我们将对应用进行简单的更新。
- 在 KMPGetStartedCodelab 项目中,打开位于
androidApp/src/main/java/com/example/kmp/getstarted/android/MainActivity.kt
的MainActivity
文件 - 修改内容
Text
composable,将platform()
信息包含在显示的字符串中。
Text(
"Hello ${platform()}",
)
- 在键盘上点击
⌥(option)+return
并选择Import function 'platform'
- 在 Android 设备或模拟器上构建并运行应用。
此更新检查应用是否可以调用 shared
模块中的 platform()
函数,该函数在 Android 平台上运行时应返回 "Android"
。
6. 将共享模块设置到 iOS 应用
Swift 无法像 Android 应用那样直接使用 Kotlin 模块,它需要生成一个编译好的二进制框架 (XCFramework bundle)。XCFramework bundle 是一个二进制软件包,包含构建多个 Apple 平台所需的框架和库。
如何分发共享库
Android Studio 中的新模块模板已配置共享模块以针对每个 iOS 架构生成一个框架。您可以在 shared
模块的 build.gradle.kts
文件中找到以下代码。
val xcfName = "sharedKit"
iosX64 {
binaries.framework {
baseName = xcfName
}
}
iosArm64 {
binaries.framework {
baseName = xcfName
}
}
iosSimulatorArm64 {
binaries.framework {
baseName = xcfName
}
}
在 iOS 项目中链接共享库
此步骤包括设置 Xcode 运行脚本以生成 Kotlin 框架,并在 iOS 应用中调用 platform()
函数。
要使用共享库,您需要按照以下步骤将 Kotlin 框架连接到 iOS 项目
- 在 Xcode 中打开 iOS 项目(即之前提到的
iosApp
目录),然后双击项目导航器中的项目名称以打开项目设置 - 在项目设置的 Build Phases 选项卡上,点击 + 并选择 New Run Script Phase。这会在所有其他阶段之后添加一个新的“运行脚本”阶段。
- 双击 Run Script 标题进行重命名。将默认的 Run Script 名称更改为 Compile Kotlin Framework,以便清楚此阶段的作用。
- 展开构建阶段,并在 Shell 下方的文本字段中输入脚本代码
cd "$SRCROOT/.."
./gradlew :shared:embedAndSignAppleFrameworkForXcode
- 将 Compile Kotlin Framework 阶段拖动到 Compile Sources 阶段之前。
- 通过点击 ⌘+B 或导航到 Product 菜单并选择 Build,在 Xcode 中构建项目。请注意,构建进度显示在 Xcode 顶部。
如果一切设置正确,项目将成功构建。
以这种方式设置运行脚本构建阶段,可以使您直接从 Xcode 编译 iOS 项目,无需切换到其他工具来编译共享模块。
验证代码对 shared
模块的访问
为了验证 iOS 应用可以成功访问 shared
模块中的代码,您将对应用进行与 Android 应用相同的简单更新。
- 在 iOS 项目中,在 Xcode 中,打开位于
Sources/View/ContentView.swift
的ContentView.swift
文件: - 在文件顶部添加
import sharedKit
。 - 修改
Text
视图,在显示的字符串中包含Platform_iosKt.platform()
信息,使用\(Platform_iosKt.platform())
。
以下是文件的最终结果
import SwiftUI
import sharedKit
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, \(Platform_iosKt.platform())!")
}
.padding()
}
}
- 通过点击 ⌘+R,或导航到 Product 菜单并点击 Run 来运行应用。
此更新检查 iOS 应用是否可以调用共享模块中的 platform()
函数,该函数在 iOS 平台上运行时应返回 "iOS"
。
7. 添加 Swift/Kotlin 接口增强器 (SKIE)
默认情况下,Kotlin 生成的原生接口是 Objective-C 头文件。Swift 与 Objective-C 直接兼容,但 Objective-C 不包含 Swift 或 Kotlin 的所有现代特性。
这也是为什么在前面的示例中,您无法直接在 Swift 代码中使用 platform()
调用 – KMP 无法生成全局函数,因为 Objective-C 不支持全局函数,只支持封装在类中的静态函数,因此您需要添加 Platform_iosKt
。
为了使接口更易于 Swift 使用,您可以使用Swift/Kotlin Interface Enhancer (SKIE) 工具来改进 :shared
模块的 Swift 接口。
SKIE 的常见功能包括
- 更好地支持默认参数
- 更好地支持密封层次结构(
sealed class
、sealed interface
) - 更好地支持
enum class
并在switch
语句中进行详尽处理 Flow
与AsyncSequence
之间的互操作性suspend fun
与async func
之间的互操作性
- 将
co.touchlab.skie
Gradle 插件添加到libs.versions.toml
文件
[versions]
skie = "0.10.1"
[plugins]
skie = { id = "co.touchlab.skie", version.ref = "skie" }
- 将插件添加到根目录下的
build.gradle.kts
文件
plugins {
...
alias(libs.plugins.skie) apply false
}
- 将插件添加到
:shared
模块的build.gradle.kts
文件
plugins {
...
alias(libs.plugins.skie)
}
- Gradle 同步项目
移除静态函数调用
现在,当您重新构建 iOS 应用时,可能不会立即注意到任何变化,但您可以移除 Platform_iosKt
前缀,让 platform()
函数充当全局函数。
Text("Hello, KMP! \(platform())")
此方法有效,因为 SKIE(以及其他功能)利用了 Swift API Notes,它会添加有关 API 的信息,以便从 Swift 代码中更好地使用它。
8. 恭喜您
恭喜您,您已成功将第一个共享的 Kotlin Multiplatform 代码添加到了 Android 和 iOS 项目中。虽然这只是一个最基本的起点,但您现在可以开始探索使用 KMP 共享代码的更高级功能和用例了。
下一步是什么?
在下一个Codelab 中,学习如何使用 Jetpack Room 在 Android 和 iOS 之间共享数据层。
了解详情
- 了解哪些 Jetpack 库支持 KMP
- 查看官方Kotlin Multiplatform 文档