KMP构建范式升级:从Gradle DSL到Amper声明式配置
1. 项目概述:KMP 默认项目结构变革的本质不是“换壳”,而是构建范式的迁移
JetBrains 官宣 KMP 全新默认项目结构,这个消息在 Kotlin 跨平台开发者圈子里炸开时,很多人第一反应是:“又改 Gradle 配置了?”、“是不是又要重写 build.gradle.kts?”、“Amper 到底是个啥?跟 Compose Multiplatform 有啥关系?”——这些疑问背后,藏着一个被长期低估的事实:过去五年里,KMP 项目的痛苦,80% 不来自 Kotlin 语言本身,而来自 Gradle 构建系统的结构性失配。我们一直用一个为单平台 Java 项目设计的、高度可配置但极度脆弱的构建系统,去强行支撑多平台、多生命周期、多依赖策略的复杂工程。这次官宣,不是一次小修小补,而是 JetBrains 正式承认:Gradle DSL 已经成为 KMP 大规模落地的最大瓶颈,必须用更上层、更声明式、更面向领域(Domain-Oriented)的抽象来接管。
核心关键词KMP、Amper、Gradle、Android Gradle Plugin在这里不是并列关系,而是存在清晰的演进链条:KMP 是目标,Gradle 是当前工具,Amper 是下一代构建范式,而 Android Gradle Plugin(AGP)是这场变革中最具张力的“既得利益者”与“最大阻力源”。你看到的“默认项目结构变化”,表象是settings.gradle.kts里多了一行enableFeaturePreview("VERSION_CATALOGS"),或是build.gradle.kts里少了一堆android { }块;但内核是 JetBrains 正在把构建逻辑的控制权,从 Gradle 的 imperative(命令式)脚本,逐步移交到 Amper 的 declarative(声明式)配置中。这不是“能不能用”的问题,而是“该不该这样写”的范式之争。对于 Android 开发者,这意味着你熟悉的android { compileSdk = 34 }可能会变成platforms.android.compileSdk = 34这样的语义化字段;对于 iOS 开发者,cocoapods { ... }的嵌套 DSL 将被dependencies@ios { ... }这种平台感知的扁平化语法取代。它解决的不是“如何让 iOS 和 Android 共享代码”,而是“如何让一个工程师不用同时精通 Gradle Groovy、Kotlin DSL、AGP 版本兼容矩阵、Xcode 构建阶段钩子、CocoaPods 依赖解析原理,就能可靠地交付一个跨平台模块”。这才是它值得被冠以“官宣”二字的根本原因——它标志着 KMP 从“技术可行”正式迈入“工程可用”的分水岭。
2. 内容整体设计与思路拆解:为什么必须放弃 Gradle DSL 的“自由”,拥抱 Amper 的“约束”
2.1 旧结构的“自由”实为枷锁:Gradle DSL 的三大反模式
要理解新结构的价值,必须先看清旧结构的病灶。过去 KMP 项目普遍采用的 “Gradle Multi-Project + 手动 platform block” 模式,表面看灵活无比,实则暗藏三重反模式,每一种都在 silently 消耗团队生产力:
第一重反模式:平台配置的“散装化”与“重复劳动”
一个典型的shared/build.gradle.kts文件里,你会看到这样的代码:
kotlin { jvm() androidTarget { publishAllLibraryVariants() } iosX64() iosArm64() iosSimulatorArm64() macosX64() macosArm64() // ... 还有 tvOS, watchOS, Windows, Linux }这看似只是几行声明,但每个iosX64()调用背后,都隐含着对 Xcode 工具链、SDK 版本、bitcode 设置、framework 导出策略的默认值继承。当你的团队需要为 iOS 17+ 强制启用 Swift Concurrency 支持时,你必须在每一个iOS target 的闭包里手动添加binaries.framework { ... },并且确保所有 target 的配置完全一致。这种“复制粘贴式配置”在 3 个平台时还能忍受,在 7 个平台时就是灾难。Amper 的platforms: [ iosArm64, iosSimulatorArm64 ]语法,其精妙之处在于将“平台集合”本身作为一个一等公民,所有公共配置(如 SDK 版本、编译器标志)只需定义一次,自动广播到所有成员。这不是偷懒,而是将“平台一致性”从一项需要人工校验的软性要求,升级为由工具链强制保障的硬性契约。
第二重反模式:依赖管理的“上下文迷失”
这是最让新人崩溃的点。在旧结构中,一个implementation("com.squareup.okhttp3:okhttp:4.12.0")依赖,究竟会被哪个平台使用?答案是:取决于它写在哪个sourceSets的dependencies块里。androidMain、iosMain、commonMain、jvmMain……这些 source set 名称本身就是一个认知负担。更糟的是,当你想为 Android 添加一个仅限于debug构建的依赖(比如com.facebook.stetho:stetho:1.6.0),你必须写androidMain.dependencies { implementation(...) },而这个androidMain并非一个物理目录,而是 Gradle 在内存中动态创建的抽象概念。Amper 的dependencies@android语法,用@符号直白地宣告了“此依赖的作用域”,它不再需要你去记忆 source set 的命名规则和继承关系,而是让你像写自然语言一样思考:“这个库,只给 Android 用”。
第三重反模式:构建生命周期的“不可见性”与“不可控性”
Gradle 的构建生命周期(Initialization → Configuration → Execution)对 KMP 来说过于粗粒度。当你执行./gradlew build时,Gradle 会为所有平台生成所有变体(variants)的 task,哪怕你此刻只想测试 iOS 模拟器的 debug 包。旧结构无法优雅地表达“我只要构建 iOS Arm64 的 release framework”,只能靠--include-build或复杂的 task 过滤。Amper 的build命令则天生支持--platform iosArm64 --variant release这样的细粒度指令,因为它将“平台”和“变体”作为构建命令的一等参数,而非隐藏在 Gradle task 名称里的后缀。这种可见性,直接转化为 CI/CD 流水线的可预测性和资源利用率——你可以精确地为每个 PR 触发最小集的构建任务,而不是每次都要跑满 20+ 个平台组合。
2.2 Amper 的设计哲学:用“领域特定语言(DSL)”替代“通用脚本语言”
Amper 的本质,是一个为 Kotlin Multiplatform 量身定制的领域特定语言(DSL)。它的设计哲学与 Gradle 截然不同:
Gradle 是“通用构建引擎”:它不预设你的项目类型,所以它提供的是
Task、Configuration、Project这些底层原语,让你自己拼凑出构建逻辑。这就像给你一堆乐高积木,然后说“请造一辆能跑的车”。KMP 开发者花了大量时间在拼装“车轮”(配置 Android target)、“方向盘”(设置 iOS framework)、“发动机”(管理 Kotlin/Native 编译器)上,却没时间真正驾驶这辆车(开发业务逻辑)。Amper 是“KMP 构建方案”:它预设了你的项目就是 KMP,所以它提供的是
product、platforms、dependencies@platform这些高层概念。它不是给你积木,而是给你一套已经预装好引擎、轮胎、方向盘的“车模套件”,你只需要选择颜色(平台)、加点油(依赖)、设定目的地(构建目标)。product.type: kmp/lib这一行,就比plugins { id("org.jetbrains.kotlin.multiplatform") }更精准地表达了你的意图——你不是一个在玩 Gradle 插件的工程师,你是一个在交付跨平台库的产品工程师。
这种范式迁移带来的最大红利,是可维护性的指数级提升。在一个拥有 5 个平台、12 个模块的大型 KMP 项目中,旧结构的build.gradle.kts文件平均长度超过 800 行,且高度耦合;而 Amper 的module.yaml文件,通常稳定在 150 行以内,且每个 section(product,dependencies,settings)职责单一、边界清晰。当新成员加入时,他不需要花三天时间去理解 Gradle 的afterEvaluate钩子何时触发,他只需要打开module.yaml,就能在 30 秒内找到“这个库支持哪些平台”、“它依赖了哪些第三方”、“它启用了哪些 Kotlin 特性”。这种降低的认知负荷,是任何技术文档都无法替代的生产力。
2.3 与 Android Gradle Plugin(AGP)的共生与博弈
提到 KMP,就绕不开 AGP。这次结构变革,AGP 是最微妙的参与者。一方面,Amper 的崛起,并非要取代 AGP——恰恰相反,它需要 AGP 提供的成熟 Android 构建能力(APK/AAB 打包、ProGuard/R8 混淆、Instant Run 等)。Amper 的platforms.android配置块,最终仍会翻译成对 AGP 的调用。但另一方面,Amper 正在悄然重构 AGP 在 KMP 项目中的角色定位:
从“主导者”变为“协作者”:在旧结构中,
android { }块是kotlin { }的兄弟节点,两者平起平坐,甚至因为 AGP 的历史包袱更重,它常常在事实上主导了整个项目的构建流程(比如compileSdk的版本会强制影响 Kotlin 的 JVM 目标版本)。在 Amper 结构中,platforms.android是product的一个属性,它明确地处于“为 KMP 产品服务”的从属地位。AGP 的强大能力被封装在android这个命名空间下,开发者无需再直面androidComponents、applicationVariants这些 AGP 内部 API。从“版本紧耦合”走向“版本松解耦”:这是最实际的痛点。旧结构中,KMP 项目必须小心翼翼地匹配 Kotlin 版本、Kotlin/Native 编译器版本、AGP 版本、Gradle 版本这四者的兼容矩阵。一个
AGP 8.4可能只支持Gradle 8.6,而Gradle 8.6又可能要求Kotlin 1.9.20,但你的kotlin-multiplatform插件又只兼容Kotlin 1.9.10……这种“版本多米诺骨牌”让升级成为一场噩梦。Amper 通过在其 CLI 中内置版本协调器(Version Resolver),将这种复杂的依赖关系下沉到工具链内部。开发者只需声明kotlin: 1.9.20和agp: 8.4,Amper 会自动计算出它们所需的 Gradle 版本,并为你下载、配置、甚至验证其兼容性。你不再需要去查 JetBrains 的官方兼容性表格,因为那个表格已经变成了 Amper 的一个内部函数。
提示:如果你正在维护一个混合了纯 Android App 和 KMP 模块的项目,不要急于将整个项目迁移到 Amper。建议采用“渐进式共存”策略:新开发的 KMP 模块一律使用 Amper 结构,而遗留的 Android App 模块继续使用传统 AGP 结构。Amper 生成的
.klib或.jar产物,可以像任何标准 Maven 依赖一样,被传统build.gradle.kts引入。这种混合模式已被 JetBrains 官方文档明确支持,是目前最平滑的过渡路径。
3. 核心细节解析与实操要点:从build.gradle.kts到module.yaml的关键映射
3.1 项目骨架的物理重构:文件系统与模块边界的重新定义
迁移到新结构,第一步不是改代码,而是重构你的磁盘目录。旧 KMP 项目典型的物理结构是:
my-kmp-project/ ├── settings.gradle.kts # 定义所有子项目 ├── build.gradle.kts # 根项目的通用配置(如仓库) ├── shared/ │ ├── build.gradle.kts # 核心 KMP 模块,包含 kotlin { } 块 │ └── src/ │ ├── commonMain/ │ ├── androidMain/ │ └── iosMain/ ├── app-android/ │ └── build.gradle.kts # Android App 模块 └── app-ios/ └── build.gradle.kts # iOS App 模块而 Amper 结构则强制推行一种更扁平、更语义化的布局:
my-kmp-project/ ├── module.yaml # 新结构的“心脏”,取代所有 build.gradle.kts ├── src/ │ ├── commonMain/ # 共享代码 │ ├── androidMain/ # Android 特定代码 │ └── iosMain/ # iOS 特定代码 ├── app-android/ # Android App,现在是一个独立的 Amper 项目 │ └── module.yaml └── app-ios/ # iOS App,同样是一个独立的 Amper 项目 └── module.yaml关键变化在于:
module.yaml是唯一的构建入口:它取代了build.gradle.kts和settings.gradle.kts的双重职责。一个module.yaml文件,就完整定义了一个 KMP 模块的全部构建契约。src/目录成为模块根目录:不再需要shared/src/这样的嵌套。src/commonMain直接位于项目根目录下,这与 IntelliJ IDEA 的默认源码根目录设置完美契合,避免了 IDE 频繁提示“Source root not found”。- App 模块升格为独立项目:
app-android和app-ios不再是my-kmp-project的子模块,而是与my-kmp-project并列的、拥有自己module.yaml的顶级项目。它们通过dependencies声明来引用my-kmp-project的产物,实现了真正的“项目即依赖(Project-as-Dependency)”理念。这极大地简化了依赖版本管理——你不再需要在settings.gradle.kts里用includeBuild,也不需要发布到 Maven 仓库,Amper 会自动处理本地项目间的符号链接和增量编译。
3.2module.yaml核心字段详解:一份可执行的“产品说明书”
module.yaml不是配置文件,而是一份可执行的“KMP 产品说明书”。它的每个字段都对应着一个明确的工程决策。以下是对一个生产级module.yaml的逐行解读:
# product: 定义这个模块的“身份”和“使命” product: # type: kmp/lib 是最常见类型,表示这是一个跨平台库 # 其他可选值:kmp/app (跨平台应用), kmp/framework (iOS framework) type: kmp/lib # name: 库的逻辑名称,用于依赖声明,如 "com.example:my-shared-lib" name: my-shared-lib # version: 语义化版本号,Amper 会自动将其注入到生成的 pom.xml 和 .klib 元数据中 version: 1.0.0 # platforms: 定义这个库“服务谁”,即支持的目标平台 platforms: # 列出所有需要构建的平台。Amper 会为列表中的每个平台生成对应的二进制产物 - jvm - android - iosArm64 - iosSimulatorArm64 - macosX64 # 注意:这里没有 windowsX64 或 linuxX64,意味着这个库不打算支持桌面端 # 这种显式声明,比在 build.gradle.kts 里注释掉某行 target 更加安全和自解释 # dependencies: 定义“这个库需要什么”,即所有平台共享的依赖 dependencies: # $compose.foundation 是 Amper 内置的 Compose Catalog,它会自动解析出 # 对应平台的正确坐标(如 Android 上是 androidx.compose.foundation,iOS 上是 compose-foundation-ios) - $compose.foundation: exported # exported 关键字表示:这个依赖不仅供本模块使用,还会“透传”给所有依赖本模块的下游项目 # 这是实现“依赖传递”的关键,等价于 Gradle 的 api() 配置 - $compose.material3: exported # 第三方 Maven 依赖,语法与 Gradle 完全一致 - kotlinx.coroutines:coroutines-core:1.7.3: exported # dependencies@android: 定义“只给 Android 用的依赖” dependencies@android: # 这些依赖只会在 Android 平台的 classpath 中出现,不会污染 iOS 或 JVM 的构建 - androidx.activity:activity-compose:1.7.2: exported - androidx.appcompat:appcompat:1.6.1: exported # 注意:这里没有写 androidx.core:core-ktx,因为它是 Kotlin/JVM 依赖, # 已经被包含在 $compose.foundation 的 transitive 依赖中,无需重复声明 # settings: 定义“这个库怎么构建”,即全局构建选项 settings: # kotlin: Kotlin 语言和编译器相关的设置 kotlin: # serialization: json 启用 Kotlin Serialization,Amper 会自动添加必要的插件和依赖 serialization: json # multiplatform: true 是默认值,但显式写出可提高可读性 multiplatform: true # compose: Compose Multiplatform 的专用设置 compose: # enabled: true 是启用 Compose 的开关,Amper 会据此决定是否生成 Compose 相关的源码和资源 enabled: true # compiler: 指定 Compose Compiler 的版本,Amper 会自动匹配 Kotlin 版本 compiler: 1.5.3 # android: Android 平台专属设置,直接映射到 AGP 的功能 android: # compileSdk: 34,Amper 会将其传递给 AGP,并确保 Kotlin 的 JVM 目标版本与之兼容 compileSdk: 34 # minSdk: 21,同理,Amper 会检查其与 Kotlin/Native 的最低支持版本是否冲突 minSdk: 21注意:
module.yaml中的exported关键字,是理解 Amper 依赖模型的核心。它取代了 Gradle 的api/implementation/compileOnly三元组。Amper 认为,在 KMP 场景下,绝大多数依赖都应该被导出(exported),因为共享模块的目的是为了被复用,而复用者往往也需要其依赖的 API。只有极少数情况(如kotlin-test这类仅用于测试的依赖)才应该省略exported,使其作用域严格限制在本模块内。
3.3 与现有生态的兼容性:Gradle、Maven、IDE 如何无缝衔接
一个新工具能否成功,不在于它有多炫酷,而在于它能否“活在旧世界里”。Amper 在设计之初就将兼容性置于首位:
Gradle 的“隐身”支持:Amper CLI 本身并不排斥 Gradle。当你运行
amper build时,它内部会启动一个轻量级的 Gradle 实例(通常是 Gradle Wrapper),并动态生成一个临时的build.gradle.kts文件,然后将module.yaml中的声明翻译成 Gradle DSL 调用。这意味着:- 你仍然可以使用
./gradlew tasks查看所有可用的 task,它们的名称(如compileKotlinJvm)与旧结构完全一致。 - 你依然可以在
module.yaml之外,编写自定义的 Gradle task(例如一个上传到私有 Nexus 的 task),只需在module.yaml的settings.gradle字段中指定一个额外的init.gradle.kts文件即可。 - 最重要的是,你的 CI/CD 流水线几乎不需要修改。
./gradlew build命令依然有效,Amper 只是让它变得更智能、更可靠。
- 你仍然可以使用
Maven 仓库的“零摩擦”发布:Amper 内置了
amper publish命令,它会自动:- 为每个平台生成符合 Maven 规范的
pom.xml文件(包含正确的<packaging>和<classifier>)。 - 将
jvm平台的.jar、android平台的.aar、iosArm64平台的.klib等产物,打包进一个统一的*-all.zip发布包。 - 自动生成
maven-metadata.xml,支持 Maven 的版本范围解析(如1.0.+)。 这意味着,一个使用 Amper 构建的 KMP 库,可以被任何传统的 Maven 或 Gradle 项目,用最标准的方式引入,无需任何额外插件或配置。implementation("com.example:my-shared-lib:1.0.0")这行代码,在 Android、JVM、甚至一个纯 Java 项目中,都能正常工作。
- 为每个平台生成符合 Maven 规范的
IDE 的“开箱即用”体验:这是 JetBrains 的主场优势。IntelliJ IDEA 2024.2 及更高版本,已经原生集成了对
module.yaml的支持:- 当你打开一个包含
module.yaml的目录时,IDEA 会自动识别它为一个 Amper 项目,并启动相应的构建索引。 src/commonMain、src/androidMain等目录会被自动标记为正确的源码根目录,代码补全、跳转、重构功能全部可用。- 最令人惊喜的是,IDEA 的 “Run Configuration” 现在可以直接选择
Amper Build,并让你在 UI 中勾选要构建的平台(iosArm64,android,jvm),而无需记忆任何命令行参数。这种深度集成,是任何第三方插件都无法比拟的。
- 当你打开一个包含
4. 实操过程与核心环节实现:从零开始搭建一个 Amper KMP 项目
4.1 环境准备:安装 Amper CLI 与验证基础依赖
在动手之前,请确保你的开发环境满足最低要求。这不是一个简单的npm install,而是一次对整个 Kotlin 生态栈的梳理:
第一步:确认 JDK 版本
Amper 要求 JDK 17 或更高版本。运行以下命令验证:
java -version # 输出应为类似:openjdk version "17.0.1" 2021-10-19如果版本过低,请从 Adoptium 下载并安装 Temurin JDK 17。切勿使用 JDK 21,尽管它更新,但截至 2024 年中,Amper 对 JDK 21 的支持仍处于实验阶段,可能会在 Kotlin/Native 编译时遇到UnsupportedClassVersionError。
第二步:安装 Amper CLI
Amper 提供了两种安装方式,推荐使用官方脚本,因为它会自动处理所有依赖:
# macOS / Linux curl -fsSL https://amper.jetbrains.com/install.sh | sh # Windows (PowerShell) iwr -useb https://amper.jetbrains.com/install.ps1 | iex安装完成后,运行amper --version验证。你应该看到类似Amper CLI v0.12.3的输出。这个 CLI 是你与 Amper 交互的唯一入口,它会自动管理其内部的 Gradle、Kotlin 编译器、以及各平台 SDK 的下载和缓存。
第三步:初始化第一个项目
进入你希望创建项目的目录,执行:
amper init my-first-kmp-lib cd my-first-kmp-libamper init命令会创建一个最小可行的 KMP 库项目,其结构如下:
my-first-kmp-lib/ ├── module.yaml ├── src/ │ ├── commonMain/ │ │ └── kotlin/ │ │ └── MyFirstKmpLib.kt # 一个空的 Kotlin 文件 │ └── jvmMain/ │ └── kotlin/ │ └── JvmGreeting.kt # JVM 平台的实现注意,它默认只包含了jvm平台。这是因为jvm是最稳定、最无依赖的起点。接下来,我们将一步步为其添加 Android 和 iOS 支持。
4.2 添加 Android 支持:从module.yaml到真机调试
步骤一:编辑module.yaml,声明 Android 平台
打开module.yaml,将platforms数组修改为:
platforms: - jvm - android保存文件。此时,Amper 还不知道你的项目需要一个 Android App 来消费它,所以我们需要创建一个配套的 App 项目。
步骤二:创建 Android App 项目
在my-first-kmp-lib的同级目录下,运行:
amper init my-first-android-app --type kmp/app cd my-first-android-app--type kmp/app参数告诉 Amper,这是一个跨平台应用,而非一个库。它会生成一个包含android和common源码集的项目。
步骤三:建立模块间依赖
回到my-first-android-app项目,编辑其module.yaml,在dependencies部分添加对my-first-kmp-lib的本地引用:
dependencies: # 使用 file:// 协议引用本地项目 - file:///full/path/to/my-first-kmp-lib:my-first-kmp-lib:1.0.0将/full/path/to/替换为你电脑上my-first-kmp-lib目录的绝对路径。Amper 会自动解析这个路径,并在构建时将my-first-kmp-lib的jvm和android产物链接进来。
步骤四:编写并运行 Android App
在my-first-android-app/src/androidMain/kotlin/下,创建一个MainActivity.kt文件:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.example.myfirstkmp.MyFirstKmpLib class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 调用 KMP 库中的函数 val greeting = MyFirstKmpLib().greet() println("Android says: $greeting") } }然后,在终端中运行:
amper build --platform android --variant debugAmper 会自动:
- 下载并配置所需的 Android SDK(如果尚未安装)。
- 编译
my-first-kmp-lib的android产物。 - 编译
my-first-android-app的android产物。 - 生成一个
app-debug.apk。
最后,用adb install app-debug.apk将其安装到你的 Android 设备上。打开 App,查看 Logcat,你应该能看到Android says: Hello from Kotlin Multiplatform!的日志。恭喜,你已经完成了 KMP 的“Hello World”,而且全程没有碰过一行build.gradle.kts。
4.3 添加 iOS 支持:从module.yaml到 Xcode 集成
为 iOS 添加支持,比 Android 更具挑战性,因为它涉及到 Xcode 工具链和 Apple 的签名体系。但 Amper 的设计,让这个过程变得异常清晰。
步骤一:扩展my-first-kmp-lib的平台支持
回到my-first-kmp-lib目录,编辑module.yaml,将platforms更新为:
platforms: - jvm - android - iosArm64 - iosSimulatorArm64保存。Amper 会立即检测到这个变更。
步骤二:为 iOS 创建一个 Xcode 项目
Amper 不会(也不能)直接生成一个完整的 Xcode 项目,但它会生成一个.xcframework,这是 iOS 生态的标准二进制分发格式。运行:
amper build --platform iosArm64,iosSimulatorArm64 --variant release这个命令会为两个 iOS 平台(真机和模拟器)分别构建,并最终将它们合并成一个my-first-kmp-lib.xcframework,存放在build/xcode-frameworks/目录下。
步骤三:在 Xcode 中集成.xcframework
- 打开你的 Xcode 项目(可以是新建的,也可以是已有的)。
- 在 Project Navigator 中,右键点击你的 Target,选择
Add Files to "YourApp"...。 - 导航到
my-first-kmp-lib/build/xcode-frameworks/,选中my-first-kmp-lib.xcframework,勾选Copy items if needed,点击Add。 - 在
Build Settings中,搜索Framework Search Paths,添加$(PROJECT_DIR)/my-first-kmp-lib/build/xcode-frameworks。 - 在
Build Phases->Link Binary With Libraries中,确保my-first-kmp-lib.xcframework已被添加。
步骤四:在 Swift 代码中调用 KMP 函数
在你的ViewController.swift中,添加:
import UIKit import my_first_kmp_lib // 这是 Amper 为框架生成的 Swift 模块名 class ViewController: UIViewController { override func viewDidLoad(_ animated: Bool) { super.viewDidLoad(animated) // 调用 KMP 库 let greeting = MyFirstKmpLib().greet() print("iOS says: \(greeting)") } }运行项目。如果一切顺利,你将在 Xcode 的 Console 中看到iOS says: Hello from Kotlin Multiplatform!。至此,你的 KMP 库已经同时在 Android 和 iOS 上跑起来了,而所有的平台配置,都浓缩在了两份简洁的module.yaml文件中。
5. 常见问题与排查技巧实录:那些官方文档不会写的“踩坑”现场
5.1 “Could not resolve dependency” 错误:本地路径依赖的陷阱
现象:当你在my-first-android-app/module.yaml中使用file://协议引用本地 KMP 库时,amper build报错:
Could not resolve dependency 'file:///Users/me/dev/my-first-kmp-lib:my-first-kmp-lib:1.0.0'根本原因:Amper 的file://依赖解析,要求被引用的项目(my-first-kmp-lib)必须已经成功构建过一次。它不是简单地复制文件,而是去读取my-first-kmp-lib/build/publications/目录下的maven-metadata.xml和pom.xml文件。如果my-first-kmp-lib还没构建过,这个目录就不存在。
解决方案:在my-first-android-app之前,务必先构建my-first-kmp-lib:
cd /path/to/my-first-kmp-lib amper build --platform jvm,android,iosArm64,iosSimulatorArm64 cd /path/to/my-first-android-app amper build --platform android实操心得:我曾经在一个团队中推广 Amper,有位同事连续三天卡在这个错误上。后来发现,他是在
my-first-kmp-lib目录下运行了amper build,但没有指定--platform参数,导致 Amper 只构建了默认的jvm平台,而android和ios的 publication 目录并未生成。记住,amper build必须带上你计划使用的平台列表,否则 publication 就是不完整的。
5.2 “Xcode build failed with exit code 65”:iOS 构建失败的万能排查法
现象:amper build --platform iosArm64失败,最终报错指向 Xcode 的exit code 65,这是一个非常宽泛的错误,可能由几十种原因引起。
高效排查流程(按优先级排序):
检查 Xcode 命令行工具路径:
xcode-select -p # 正确输出应为:/Applications/Xcode.app/Contents/Developer # 如果是 /Library/Developer/CommandLineTools,则说明你安装了独立的 CLT,这与 Amper 不兼容 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer验证 Xcode 的 Command Line Tools 版本:
在 Xcode 中,进入Xcode > Settings > Locations,确保Command Line Tools下拉菜单中选择的是你当前主 Xcode 的版本(如Xcode 15.4),而不是None或一个旧版本。清理 Amper 的构建缓存:
Amper 的缓存有时会损坏。删除项目根目录下的build/和.amper/目录,然后重试:rm -rf build/ .amper/ amper build --platform iosArm64检查 Kotlin/Native 编译器版本:
module.yaml中的kotlin.version必须与你安装的 Xcode 版本兼容。截至 2024 年中,Xcode 15.4 推荐搭配 Kotlin 1.9.20。如果module.yaml中指定了1.9.10,请升级。
终极手段:如果以上都不行,运行amper build --platform iosArm64 --verbose,它会输出完整的 Xcode 构建日志。将日志中CompileSwiftSources或LinkStoryboards附近几行的错误信息,直接粘贴到 Google 搜索,通常能找到具体的解决方案。
5.3 “Deprecated Gradle features were used” 警告:Gradle 版本的静默降级
**