Skip to content

项目结构与源集配置

源: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 commonMainImplementationDependenciesMetadata

IDE 中的源集显示

Android Studio/IntelliJ IDEA 会在项目视图中显示源集结构:

shared
└── src
    ├── commonMain
    ├── androidMain
    └── iosMain (聚合显示)

通过合理配置源集结构,可以在保持代码共享最大化的同时,灵活处理平台差异,构建可维护的跨平台项目。