项目结构与源集配置
源:Kotlin Multiplatform - Hierarchical Project Structure
源集(Source Sets)是 Kotlin Multiplatform 项目的核心组织单元,它决定了代码的共享范围和编译目标。理解源集的层级关系和配置方式,是掌握 KMP 项目架构的关键。
源集基本概念
什么是源集
源集是一组共享同样编译目标的源代码文件集合。每个源集都有自己的依赖、资源和配置。
commonMain (所有平台)
↓
├─ androidMain (仅 Android)
├─ iosMain (iOS 所有架构)
│ ↓
│ ├─ iosArm64Main
│ ├─ iosX64Main
│ └─ iosSimulatorArm64Main
└─ jvmMain (JVM 平台)源集配置示例
kotlin
kotlin {
// 定义目标平台
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
jvm()
sourceSets {
// 公共源集
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
// 平台特定源集
val androidMain by getting {
dependencies {
implementation("androidx.core:core-ktx:1.13.1")
}
}
val iosMain by creating {
dependsOn(commonMain)
}
val iosX64Main by getting {
dependsOn(iosMain)
}
val iosArm64Main by getting {
dependsOn(iosMain)
}
val iosSimulatorArm64Main by getting {
dependsOn(iosMain)
}
}
}源集层级关系
默认层级结构
从 Kotlin 1.9.20 开始,默认启用层级项目结构:
commonMain
↓
├─ appleMain (Apple 平台共享)
│ ↓
│ ├─ iosMain
│ │ ↓
│ │ ├─ iosArm64Main
│ │ ├─ iosX64Main
│ │ └─ iosSimulatorArm64Main
│ │
│ ├─ macosMain
│ │ ↓
│ │ ├─ macosArm64Main
│ │ └─ macosX64Main
│ │
│ └─ tvosMain, watchosMain ...
│
├─ androidMain
├─ jvmMain
└─ jsMain创建中间源集
中间源集允许在多个平台之间共享代码,而不是直接在所有平台共享:
kotlin
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
// 自动创建的中间源集
val commonMain by getting
// iOS 平台共享源集(自动创建)
val iosMain by created {
dependsOn(commonMain)
}
// 手动创建的中间源集
val mobileMain by creating {
dependsOn(commonMain)
}
val androidMain by getting {
dependsOn(mobileMain)
}
val iosMain by getting {
dependsOn(mobileMain)
}
}
}dependsOn 关系详解
dependsOn 定义了源集之间的继承关系:
kotlin
val iosMain by getting {
dependsOn(commonMain) // iosMain 可以访问 commonMain 中的所有声明
}规则:
- 子源集可以访问父源集的所有 API
- 子源集继承父源集的依赖
- 子源集必须实现父源集中的所有
expect声明
源集目录结构
标准项目布局
shared/
├── build.gradle.kts
└── src/
├── commonMain/
│ ├── kotlin/
│ └── resources/
├── commonTest/
│ ├── kotlin/
│ └── resources/
├── androidMain/
│ ├── kotlin/
│ ├── res/
│ └── AndroidManifest.xml
├── androidTest/
│ └── kotlin/
├── iosMain/
│ ├── kotlin/
│ └── resources/
├── iosTest/
│ └── kotlin/
├── iosX64Main/
│ └── kotlin/
├── iosArm64Main/
│ └── kotlin/
└── iosSimulatorArm64Main/
└── kotlin/源集命名约定
- Main 源集:
<target>Main(如commonMain,androidMain) - Test 源集:
<target>Test(如commonTest,iosTest) - 中间源集:自定义名称(如
jvmAndAndroid,nativeMain)
源集依赖管理
依赖声明方式
kotlin
kotlin {
sourceSets {
commonMain.dependencies {
// 实现依赖
implementation("io.ktor:ktor-client-core:3.0.3")
// API 依赖(会传递给依赖当前模块的模块)
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
// 仅编译依赖
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
androidMain.dependencies {
implementation("io.ktor:ktor-client-android:3.0. 3")
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:3.0.3")
}
}
}toml
# gradle/libs.versions.toml
[versions]
ktor = "3.0.3"
kotlinx-serialization = "1.7.3"
[libraries]
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
# build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.ktor.client.core)
api(libs.kotlinx.serialization.json)
}
}
}平台特定依赖
kotlin
kotlin {
sourceSets {
val androidMain by getting {
dependencies {
// Android 专用库
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("com.google.android.material:material:1.12.0")
}
}
val iosMain by getting {
dependencies {
// iOS 专用库(通过 CocoaPods 或 SPM)
// 注:Native 库通常通过 cinterop 集成
}
}
val jvmMain by getting {
dependencies {
// JVM 专用库
implementation("org.jetbrains.exposed:exposed-core:0.58.0")
}
}
}
}高级配置技巧
条件性源集配置
kotlin
kotlin {
sourceSets {
all {
languageSettings {
// 启用实验性 API
optIn("kotlin.RequiresOptIn")
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
}
}
val commonMain by getting {
// 仅在 commonMain 中启用特定特性
languageSettings {
progressiveMode = true
}
}
}
}共享 JVM 和 Android 代码
kotlin
kotlin {
androidTarget()
jvm()
sourceSets {
val jvmAndroid by creating {
dependsOn(commonMain)
}
val androidMain by getting {
dependsOn(jvmAndroid)
}
val jvmMain by getting {
dependsOn(jvmAndroid)
}
}
}多目标架构优化
对于 iOS,可以减少冗余配置:
kotlin
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
kotlin {
// 定义所有 iOS 目标
val iosTargets = listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
)
// 统一配置
iosTargets.forEach { target ->
target.binaries.framework {
baseName = "SharedSDK"
isStatic = true
}
}
sourceSets {
// iOS 公共源集会自动创建
val iosMain by getting {
dependencies {
// 所有 iOS 架构共享的依赖
}
}
}
}源集编译顺序
编译器按照依赖关系编译源集:
1. commonMain (最先编译)
2. 中间源集 (如 iosMain, jvmAndroid)
3. 平台源集 (如 iosArm64Main, androidMain)这意味着:
commonMain不能依赖任何平台代码- 中间源集可以访问
commonMain的 API - 平台源集可以访问所有父源集的 API
常见配置模式
模式 1:简单双平台项目
kotlin
kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain {
dependencies {
implementation(libs.kotlinx.coroutines.core)
}
}
// iOS 源集自动创建并配置
}
}模式 2:全平台项目
kotlin
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
jvm()
js(IR) {
browser()
nodejs()
}
sourceSets {
commonMain.dependencies {
implementation(libs.ktor.client.core)
}
val jvmAndAndroid by creating {
dependsOn(commonMain)
dependencies {
implementation(libs.slf4j.api)
}
}
androidMain {
dependsOn(jvmAndAndroid)
}
jvmMain {
dependsOn(jvmAndAndroid)
}
}
}模式 3:共享测试代码
kotlin
kotlin {
sourceSets {
commonTest.dependencies {
implementation(kotlin("test"))
}
val commonIntegrationTest by creating {
dependsOn(commonTest)
dependencies {
implementation(libs.ktor.client.mock)
}
}
val androidInstrumentedTest by getting {
dependsOn(commonIntegrationTest)
}
val iosTest by getting {
dependsOn(commonIntegrationTest)
}
}
}最佳实践
✅ 实践 1:最大化代码共享
尽可能将代码放在 commonMain,只在必要时使用平台特定源集。
kotlin
// ✅ 推荐:大部分逻辑在 commonMain
// commonMain
class UserRepository(private val storage: Storage) {
suspend fun getUser(): User = storage.load()
}
expect fun createStorage(): Storage
// ❌ 不推荐:所有逻辑都分散在平台代码中✅ 实践 2:使用中间源集减少重复
kotlin
// ✅ 使用 appleMain 共享 iOS/macOS 代码
val appleMain by getting {
dependencies {
// Apple 平台专用依赖
}
}
// ❌ 在每个 Apple 平台重复配置✅ 实践 3:统一依赖版本管理
使用 Version Catalog 管理所有依赖版本,避免平台间版本不一致。
✅ 实践 4:明确源集职责
- commonMain:纯 Kotlin 业务逻辑
- platformMain:平台 API 调用和
expect实现 - 中间源集:跨部分平台的共享逻辑
❌ 避免过度嵌套
不要创建过多层级的中间源集,保持结构清晰:
kotlin
// ❌ 过度嵌套
commonMain → mobileMain → smartphoneMain → iosMain → iosArm64Main
// ✅ 合理层级
commonMain → iosMain → iosArm64Main源集配置调试
查看源集结构
使用 Gradle 任务查看项目的源集配置:
bash
./gradlew :shared:sourceSets检查依赖关系
bash
./gradlew :shared:dependencies --configuration commonMainImplementationDependenciesMetadataIDE 中的源集显示
Android Studio/IntelliJ IDEA 会在项目视图中显示源集结构:
shared
└── src
├── commonMain
├── androidMain
└── iosMain (聚合显示)通过合理配置源集结构,可以在保持代码共享最大化的同时,灵活处理平台差异,构建可维护的跨平台项目。