3. Compose Multiplatform项目初始化

原理概述

Compose Multiplatform 项目的初始化核心在于 共享模块(shared module) 的 Gradle 配置。该模块必须声明 Compose 插件、多平台目标(Android / iOS)、以及所需的外部依赖(Ktor、ViewModel 等)。依赖管理推荐使用 版本目录(Version Catalog) 统一管理版本号,避免硬编码。

本章将详细拆解从根项目到 shared 模块的完整配置流程,涵盖 Ktor HTTP 客户端与 ViewModel 生命周期的依赖引入方式。

Compose Multiplatform 项目初始化流程与依赖关系 Compose Multiplatform 项目初始化流程与依赖关系 根项目 build.gradle.kts shared 模块 build.gradle.kts Compose Multiplatform 插件 Ktor 客户端依赖 ViewModel 依赖 Android / iOS 平台配置 提示:shared 模块是依赖注入的中心,Ktor 和 ViewModel 均在此模块声明 commonMain 依赖,平台配置仅处理 expect/actual

Gradle 配置:根项目与 shared 模块

根项目 build.gradle.kts

根项目负责声明插件版本与 版本目录 的入口。推荐使用 libs.versions.toml 管理所有依赖版本。

plugins {
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.kotlinMultiplatform) apply false
    alias(libs.plugins.composeMultiplatform) apply false
    alias(libs.plugins.composeCompiler) apply false
}

// settings.gradle.kts 中添加版本目录引用
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

shared 模块 build.gradle.kts

这是整个项目的核心。必须应用 kotlin("multiplatform")、id("org.jetbrains.compose") 以及 compose.compiler 插件。同时声明 Android、iOS 目标。

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions { jvmTarget = "17" }
        }
    }
    listOf(iosX64(), iosArm64(), iosSimulatorArm64()).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "shared"
            isStatic = true
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3)
            implementation(libs.ktor.client.core)
            implementation(libs.viewmodel.compose)
        }
        androidMain.dependencies {
            implementation(libs.ktor.client.okhttp)
        }
        iosMain.dependencies {
            implementation(libs.ktor.client.darwin)
        }
    }
}

android {
    namespace = "com.example.shared"
    compileSdk = 34
    defaultConfig { minSdk = 24 }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

依赖管理:版本目录(Version Catalog)

在项目的 gradle/libs.versions.toml 中集中定义版本号。典型内容如下:

[versions]
kotlin = "2.0.21"
compose-multiplatform = "1.7.1"
ktor = "3.0.2"
viewmodel = "0.12.0"
coroutines = "1.9.0"

[libraries]
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "viewmodel" }

[plugins]
androidApplication = { id = "com.android.application", version = "8.7.3" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
androidLibrary = { id = "com.android.library", version = "8.7.3" }
最佳实践:版本目录文件应提交至版本控制,团队统一使用。不要手动修改 build.gradle.kts 中的硬编码版本,仅通过 libs.versions.toml 管理。

Ktor 客户端配置

在 commonMain 中声明 Ktor 核心依赖后,需要为平台提供实际的 Engine 实现。典型的 expect/actual 模式:

// commonMain 中的 expect 声明
expect fun createPlatformHttpClient(): HttpClient

// androidMain 中的 actual
actual fun createPlatformHttpClient(): HttpClient {
    return HttpClient(OkHttp) {
        install(ContentNegotiation) { json() }
    }
}

// iosMain 中的 actual
actual fun createPlatformHttpClient(): HttpClient {
    return HttpClient(Darwin) {
        install(ContentNegotiation) { json() }
    }
}

此外,也可在 commonMain 中直接使用 HttpClient() 配合 ktor-client-core,但 不推荐——缺失平台 Engine 会在运行时崩溃。务必始终通过 expect/actual 提供平台版本。

ViewModel 配置

Compose Multiplatform 的 ViewModel 由 lifecycle-viewmodel-compose 库提供。该库已在 commonMain dependencies 中引入。使用时需注意:

  • ViewModel 在 commonMain 中定义为普通类,继承 ViewModel()(来自 androidx.lifecycle.ViewModel)。
  • 通过 viewModel() 函数在 Composable 中获取实例——该函数由 lifecycle-viewmodel-compose 提供。
  • 无需在 Android 侧额外添加依赖(该库已为 Android 提供实际实现)。
// commonMain 中的 ViewModel 示例
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count = _count.asStateFlow()

    fun increment() {
        _count.value += 1
    }
}

// Compose 中使用
@Composable
fun CounterScreen() {
    val vm: CounterViewModel = viewModel()
    val count by vm.count.collectAsState()
    // UI ...
}

调试技巧

  • Gradle 构建分析: 使用 --info 或 --scan 参数查看依赖解析详情。若出现版本冲突,优先检查 libs.versions.toml 中的 Ktor 和 ViewModel 版本是否与 Compose 版本兼容。
  • iOS 端测试: 确保 iosMain 的 Ktor Engine 依赖正确(ktor-client-darwin)。运行 iOS 模拟器时如果报错 Module not found: darwin,请清理 Gradle 缓存并重新同步。
  • Ktor 日志: 添加 Logging 插件可以输出 HTTP 请求/响应细节:install(Logging) { level = LogLevel.BODY }。

常见错误

错误现象根本原因解决方案
Unresolved reference: ktor.client.core 版本目录中未声明 Ktor 库或版本号错误 检查 libs.versions.toml 的 [libraries] 部分,确保 ktor-client-core 条目正确
Symbol 'ViewModel' is unresolved lifecycle-viewmodel-compose 依赖未添加至 commonMain 在 commonMain.dependencies 中添加 implementation(libs.viewmodel.compose)
iOS 编译失败:Undefined symbols for architecture arm64 iOS 端 Ktor Engine 缺失 在 iosMain.dependencies 中添加 ktor-client-darwin
Android 端编译时提示 Duplicate class Ktor 的多个 Engine 被同时引入(如 okhttp + android) 只保留 androidMain 中一个 Engine 依赖,移除其他平台 Engine

性能与稳定性优化

  • Ktor 插件按需安装: 仅在需要时添加 ContentNegotiation、Logging 等插件,避免不必要的序列化开销。
  • ViewModel 作用域: 使用 viewModelScope 管理协程生命周期,避免在 Composable 中直接启动协程导致内存泄漏。
  • 依赖版本锁定: 将 libs.versions.toml 中的所有版本统一为稳定版,避免使用 + 通配符。例如:ktor = "3.0.2" 而非 3.0.+。
  • Android 混淆规则: 若启用 R8 / ProGuard,需在 consumer-rules.pro 中添加 Ktor 与 ViewModel 的保留规则:
-keepclassmembers class * extends androidx.lifecycle.ViewModel {
    <init>(...);
}
-keep class io.ktor.** { *; }
注意: 在不同 Kotlin 版本下,Compose Multiplatform 插件与 ViewModel 库的兼容性可能变化。请始终参考 Compose Multiplatform 版本发布页 确认推荐组合。