Kotlin多平台开发入门

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]。

KMP项目结构与构建配置

Kotlin多平台项目采用模块化架构,核心包含三大模块:shared(共享逻辑)androidApp(Android应用)iosApp(iOS应用),部分项目会扩展jsApp(Web)或jvmApp(桌面)模块[37][38]。这种结构通过源代码集(Source Sets) 实现跨平台代码隔离与共享,其中commonMain存放平台无关逻辑,androidMain/iosMain等存放平台特定实现,形成"共享代码为核心、平台代码为补充"的层次化架构。

KMP项目典型目录结构

plaintext
复制代码
.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定义多平台目标与依赖关系。以下是典型配置示例,需注意插件版本与目标平台声明的匹配性:

kotlin
复制代码
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实现
            }
        }
    }
}

构建配置关键点

  1. 目标平台声明:需根据项目需求选择android()iosArm64()等,避免冗余目标拖慢编译速度
  2. 依赖管理commonMain仅添加跨平台库,平台特定库需在对应sourceSet中声明
  3. 编译器优化:Kotlin 1.9.0起可通过gradle.propertieskotlin.experimental.tryK2=true启用K2编译器加速构建[39]

项目初始化推荐使用Kotlin Multiplatform Wizard[40])生成模板,可自动配置上述模块结构与依赖关系[36]。

Compose Multiplatform跨平台UI实现

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统一应用外观,布局组件(如ColumnRow)控制界面结构,修饰符(Modifier)调整组件属性。以下是跨平台"时间显示"组件示例,包含文本与按钮交互:

kotlin
复制代码
@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("更新时间")
            }
        }
    }
}

上述代码中,remembermutableStateOf实现状态管理(类似Jetpack Compose),当currentTime变化时,Text组件会自动重组刷新。Modifier链式调用控制布局行为,其调用顺序直接影响最终效果(如先fillMaxWidthpadding会保留内边距空间)[42]。

平台特定启动代码是UI共享的关键补充。Android通过MainActivity启动Compose UI,iOS通过ComposeUIViewController将Compose组件转换为原生视图控制器,桌面端则通过main函数创建窗口:

kotlin
复制代码
// 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视频播放器:

kotlin
复制代码
@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调用等。以下是跨平台日志工具的实现示例:

kotlin
复制代码
// 共享模块: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的用户数据请求示例:

kotlin
复制代码
// 共享模块: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自动解析,避免手动编写平台特定的解析代码。在commonMainbuild.gradle.kts中添加依赖:

kotlin
复制代码
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]。

性能优化方面,建议:

  1. 启用K2编译器(gradle.propertieskotlin.experimental.tryK2=true),提升多平台项目编译速度[39]
  2. 合理规划sourceSet依赖,避免commonMain引入平台特定库
  3. 使用@OptIn(ExperimentalMultiplatform::class)标注实验性API,便于后续迁移
  4. 桌面端启用热重载加速UI调试(通过IDE插件支持)[35]

Kotlin多平台开发正在快速成熟,2024年Google I/O已将其纳入官方支持体系,Jetpack库(如Room、WorkManager)逐步提供多平台API[16]。对于Android开发者,掌握KMP意味着技能边界的扩展,可通过单一技术栈覆盖多端开发,显著提升开发效率与代码质量。