Kotlin多平台开发(Kotlin Multiplatform, KMP)是JetBrains推出的跨平台解决方案,通过单一代码库实现Android、iOS、桌面端(Windows/MacOS/Linux)及Web平台的业务逻辑与UI共享。其核心优势在于保留平台原生特性的同时最大化代码复用率,据JetBrains案例显示,采用KMP的应用平均可共享80%-96%的代码,如Respawn习惯教练共享96%代码,Fast&Fit应用共享超90%代码库(含完整UI)[35]。与Flutter等跨平台框架不同,KMP通过编译为原生二进制(iOS机器码/Android字节码/WebAssembly)实现平台适配,适合渐进式改造现有项目,尤其适合需要访问原生API(如地图、相机)的场景[36]。
Kotlin多平台项目采用模块化架构,核心包含三大模块:shared(共享逻辑)、androidApp(Android应用)、iosApp(iOS应用),部分项目会扩展jsApp(Web)或jvmApp(桌面)模块[37][38]。这种结构通过源代码集(Source Sets) 实现跨平台代码隔离与共享,其中commonMain存放平台无关逻辑,androidMain/iosMain等存放平台特定实现,形成"共享代码为核心、平台代码为补充"的层次化架构。
KMP项目典型目录结构
.idea/ // IDE配置文件
build.gradle.kts // 主构建脚本
settings.gradle.kts // 子模块配置
shared/ // 共享逻辑模块
src/
commonMain/ // 跨平台共享代码(业务逻辑、数据模型、Compose UI)
commonTest/ // 共享测试代码
androidMain/ // Android平台特定代码(如AMS调用、Android日志)
iosMain/ // iOS平台特定代码(如UIKit交互、iOS日志)
androidApp/ // Android应用模块(含Activity、资源文件)
iosApp/ // iOS应用模块(含Xcode项目、Swift代码)
共享模块配置是KMP项目的核心,通过build.gradle.kts定义多平台目标与依赖关系。以下是典型配置示例,需注意插件版本与目标平台声明的匹配性:
plugins {
id("org.jetbrains.kotlin.multiplatform") version "1.9.0" // KMP插件
id("org.jetbrains.compose") version "1.7.0" // Compose Multiplatform插件
}
kotlin {
// 声明目标平台
android() // Android平台
iosArm64() // iOS真机架构
iosX64() // iOS模拟器架构
js(BOTH) { browser() } // Web平台(可选)
sourceSets {
// 共享代码依赖配置
val commonMain by getting {
dependencies {
implementation("org.jetbrains.compose:compose-runtime:1.7.0") // Compose运行时
implementation("io.ktor:ktor-client-core:2.3.3") // Ktor网络核心
}
}
// Android平台特定依赖
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:2.3.3") // Android Ktor实现
}
}
// iOS平台特定依赖
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.3") // iOS Ktor实现
}
}
}
}
构建配置关键点
android()、iosArm64()等,避免冗余目标拖慢编译速度 commonMain仅添加跨平台库,平台特定库需在对应sourceSet中声明 gradle.properties中kotlin.experimental.tryK2=true启用K2编译器加速构建[39]项目初始化推荐使用Kotlin Multiplatform Wizard[40])生成模板,可自动配置上述模块结构与依赖关系[36]。
Compose Multiplatform基于Jetpack Compose扩展而来,通过声明式UI实现Android、iOS、桌面端的UI共享。其核心优势在于Android技能复用,开发者可直接迁移Jetpack Compose的状态管理、布局系统与Material组件至其他平台,同时支持平台特定UI定制[35]。截至2025年,Compose Multiplatform for iOS已进入稳定版本,支持原生交互(如文字选择、拖拽)、可变字体及热重载,可用于生产环境[41]。
基础UI组件实现遵循Jetpack Compose语法,通过@Composable注解标识可组合函数,使用MaterialTheme统一应用外观,布局组件(如Column、Row)控制界面结构,修饰符(Modifier)调整组件属性。以下是跨平台"时间显示"组件示例,包含文本与按钮交互:
@Composable
@Preview
fun TimeDisplayScreen() {
MaterialTheme(colors = MaterialTheme.colorScheme) {
var currentTime by remember { mutableStateOf("未选择位置") } // 状态管理
Column(
modifier = Modifier
.safeContentPadding() // 适配平台安全区域
.fillMaxSize() // 占满父容器
.verticalScroll(rememberScrollState()), // 垂直滚动
horizontalAlignment = Alignment.CenterHorizontally, // 水平居中
verticalArrangement = Arrangement.Center // 垂直居中
) {
Text(
text = currentTime,
style = MaterialTheme.typography.headlineMedium, // 平台统一字体样式
modifier = Modifier.padding(vertical = 16.dp)
)
Button(
onClick = {
currentTime = "当前时间: ${LocalDateTime.now()}"
PlatformLogger().log("时间更新: $currentTime") // 调用平台日志
},
modifier = Modifier.padding(top = 8.dp)
) {
Text("更新时间")
}
}
}
}
上述代码中,remember与mutableStateOf实现状态管理(类似Jetpack Compose),当currentTime变化时,Text组件会自动重组刷新。Modifier链式调用控制布局行为,其调用顺序直接影响最终效果(如先fillMaxWidth再padding会保留内边距空间)[42]。
平台特定启动代码是UI共享的关键补充。Android通过MainActivity启动Compose UI,iOS通过ComposeUIViewController将Compose组件转换为原生视图控制器,桌面端则通过main函数创建窗口:
// Android启动代码(androidApp模块)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { TimeDisplayScreen() } // 设置Compose根组件
}
}
// iOS启动代码(iosApp模块)
fun MainViewController(): UIViewController = ComposeUIViewController {
TimeDisplayScreen() // 嵌入Compose组件
}
// 桌面启动代码(jvmApp模块)
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "跨平台时间显示"
) {
TimeDisplayScreen() // 桌面窗口内容
}
}
对于需要原生组件交互的场景,Compose Multiplatform支持双向集成:既可将SwiftUI/UIKit组件嵌入Compose(通过UIKitView),也可将Compose组件嵌入SwiftUI(通过UIViewControllerRepresentable)[43]。例如在Compose中嵌入iOS视频播放器:
@Composable
fun IosVideoPlayer(videoUrl: String) {
UIKitView(
factory = { // 创建原生视图
val player = AVPlayer(URL(string = videoUrl))
val playerVC = AVPlayerViewController().apply {
this.player = player
player.play()
}
playerVC.view
},
modifier = Modifier.fillMaxSize()
)
}
KMP通过**expect/actual机制**解决平台特定逻辑差异,允许在共享代码中声明接口(expect),在各平台模块中提供具体实现(actual)。典型应用场景包括日志系统、权限申请、原生API调用等。以下是跨平台日志工具的实现示例:
// 共享模块:commonMain(声明接口)
expect class PlatformLogger() {
fun log(message: String) // 平台通用日志方法
fun logError(message: String, throwable: Throwable) // 带异常的日志重载
}
// Android平台:androidMain(实际实现)
actual class PlatformLogger actual constructor() {
actual fun log(message: String) {
android.util.Log.d("KMPDemo", message) // 使用Android原生Log
}
actual fun logError(message: String, throwable: Throwable) {
android.util.Log.e("KMPDemo", message, throwable)
}
}
// iOS平台:iosMain(实际实现)
actual class PlatformLogger actual constructor() {
actual fun log(message: String) {
println("KMPDemo: $message") // iOS通过println输出日志
}
actual fun logError(message: String, throwable: Throwable) {
println("KMPDemo Error: $message, 异常: ${throwable.localizedDescription}")
}
}
expect/actual使用规范
expect声明仅能出现在commonMain或平台无关sourceSet中 actual实现必须与expect声明完全匹配(函数签名、参数类型、返回值) actual typealias映射平台类型网络共享是KMP业务逻辑复用的核心场景,推荐使用Ktor客户端实现跨平台网络请求。Ktor提供统一API抽象,底层自动适配平台网络栈(Android使用OkHttp,iOS使用NSURLSession,桌面使用Java HttpClient)。以下是基于Ktor的用户数据请求示例:
// 共享模块:commonMain
data class User( // 跨平台数据模型
val id: String,
val name: String,
val email: String
)
class UserRepository(private val httpClient: HttpClient) {
suspend fun fetchUser(userId: String): Result<User> {
return try {
val response = httpClient.get("https://api.example.com/users/$userId") {
headers {
append("Authorization", "Bearer ${PlatformConfig.apiKey}") // 平台配置
}
}
Result.success(response.body()) // 自动解析JSON(需配置序列化插件)
} catch (e: Exception) {
PlatformLogger().logError("获取用户失败", e)
Result.failure(e)
}
}
}
// 共享模块:commonMain(HttpClient配置)
fun createHttpClient(): HttpClient {
return HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true }) // JSON序列化
}
install(Logging) { // 日志拦截器
level = LogLevel.INFO
logger = object : Logger {
override fun log(message: String) {
PlatformLogger().log("HTTP: $message") // 调用平台日志
}
}
}
}
}
上述代码中,User数据类在各平台自动适配,UserRepository封装网络请求逻辑,createHttpClient提供跨平台客户端配置。Ktor通过插件化设计支持JSON解析、日志、缓存等功能,其共享API确保业务逻辑无需修改即可运行于多平台[44]。
数据模型统一是网络共享的基础,推荐使用Kotlinx Serialization插件实现JSON自动解析,避免手动编写平台特定的解析代码。在commonMain的build.gradle.kts中添加依赖:
dependencies {
implementation("io.ktor:ktor-client-content-negotiation:2.3.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.3")
}
通过上述配置,Ktor可自动将JSON响应映射为User对象,实现"一次定义,多端复用"的数据解析逻辑。
KMP生态已积累丰富实践案例,涵盖电商、教育、社交等多个领域。Markaz(巴基斯坦电商平台) 采用Compose Multiplatform构建100+屏幕,实现500万+下载量[35];Physics Wallah(教育平台) 共享UI与业务逻辑,服务1700万活跃用户[35];BiliBili则通过KMP实现跨平台即时通讯功能[35]。这些案例验证了KMP在生产环境的可靠性。
渐进式采用是KMP落地的推荐策略:先从独立功能模块(如网络请求、数据解析)入手,逐步扩展至UI层。例如,可先将Android项目的网络层迁移至shared模块,验证稳定性后再扩展UI组件共享。这种方式可降低风险,同时让团队逐步熟悉KMP开发模式[35]。
性能优化方面,建议:
gradle.properties中kotlin.experimental.tryK2=true),提升多平台项目编译速度[39] commonMain引入平台特定库 @OptIn(ExperimentalMultiplatform::class)标注实验性API,便于后续迁移 Kotlin多平台开发正在快速成熟,2024年Google I/O已将其纳入官方支持体系,Jetpack库(如Room、WorkManager)逐步提供多平台API[16]。对于Android开发者,掌握KMP意味着技能边界的扩展,可通过单一技术栈覆盖多端开发,显著提升开发效率与代码质量。