摘要: Kotlin Multiplatform (KMP) 是目前我们选定的跨平台开发方案,但手头有几百万行代码的存量项目,不可能推倒重来。本文将通过"组件化集成"的思路,从零环境搭建,到处理平台差异,最后输出 AAR 和 XCFramework,实现 KMP 在大型项目中的"增量"落地。
前言:为什么是组件化集成?
KMP 的最大魅力在于它的非侵入性 。在"Brownfield(棕地/存量)"开发模式下,KMP 可以仅仅作为一个 Module(模块) 存在。
- 对 Android 来说: 它就是一个普通的 Kotlin Library,输出
.aar。 - 对 iOS 来说: 它就是一个第三方 SDK,输出
.xcframework。
这意味着:不需要重写 APP,只需要把"核心算法"、"数据层"或"新业务模块"用 KMP 写一遍,然后分发给双端使用。
第一部分:从 0 到 1 的环境准备
在开始写代码前,必须跨过环境配置这道坎。KMP 涉及双端生态,配置稍显复杂。
1. 硬件与软件清单
-
操作系统: 推荐 macOS(必须用于编译 iOS 产物)。如果是 Windows,只能编译 Android 和 JVM 端。
-
JDK: 强烈推荐 JDK 17,兼容性最佳。
-
Android Studio: 最新稳定版。需在 SDK Manager 安装
CMake和Command-line Tools。 -
Xcode: App Store 下载最新版(仅限 Mac)。安装后务必执行:
Bash
csharpsudo xcode-select -s /Applications/Xcode.app/Contents/Developer
2. 快速创建项目
不要手动去配置 Gradle 文件,那是噩梦的开始。请直接使用官方向导:
- 访问 kmp.jetbrains.com。
- 填写 Project ID。
- iOS framework distribution 选择
Regular framework(适合手动集成)或CocoaPods。 - 下载并解压,使用 Android Studio 打开。
避坑指南: 第一次打开项目,Gradle Sync 可能会跑很久,请确保网络通畅。直到左侧 Project 视图没有红线报错,才算成功。
第二部分:架构设计与平台差异化处理
进入开发阶段,最核心的问题是:什么代码写在公用层,什么代码需要单独写?
1. 代码分层策略
KMP 项目通常包含三个核心 SourceSet:
commonMain(90% 代码) :业务逻辑、网络请求 (Ktor)、数据库 (SQLDelight/Room)、通用 UI (Compose Multiplatform)。androidMain:Android 特有实现。iosMain:iOS 特有实现。
2. 什么时候需要"单独处理"?
只要涉及**"问操作系统要权限、要硬件、要特定服务"**的场景,就需要分离。
我们使用 expect (定义接口) 和 actual (平台实现) 关键字:
| 场景 | 是否需要独立处理 | 解决方案 |
|---|---|---|
| 纯业务逻辑 / ViewModel | ❌ 不需要 | 直接写在 commonMain |
| 网络请求 / JSON 解析 | ❌ 不需要 | 使用 Ktor + Kotlinx.serialization |
| 相机 / 相册 / 蓝牙 | ✅ 需要 | common 定义接口,平台端调用 CameraX/AVFoundation |
| WebView / 地图 | ✅ 需要 | CMP 中使用 AndroidView 和 UIKitView 互操作 |
| 支付 SDK (微信/支付宝) | ✅ 需要 | 封装原生 SDK 的调用接口 |
第三部分:核心实战------构建组件化产物
这是本文的重点。我们需要配置 build.gradle.kts,让 KMP 模块自动生成 Android 和 iOS 可以直接依赖的二进制包。
1. 配置 shared/build.gradle.kts
将以下配置应用到 KMP 模块中:
Kotlin
scss
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
`maven-publish` // 用于发布 AAR
}
kotlin {
androidTarget {
publishLibraryVariants("release", "debug") // 允许发布 Android 变体
compilations.all {
kotlinOptions { jvmTarget = "1.8" }
}
}
// 配置 iOS XCFramework 输出
val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())
iosTargets.forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "SharedSDK" // iOS 引用时的框架名
isStatic = true // 建议静态库,集成简单
}
}
// 自动注册 XCFramework 任务
val xcf = XCFramework("SharedSDK")
iosTargets.forEach {
it.binaries.framework { xcf.add(this) }
}
// ... sourceSets 配置 ...
}
// 配置 Android Maven 发布
publishing {
publications {
create<MavenPublication>("mavenAndroid") {
groupId = "com.company.kmp"
artifactId = "shared-sdk"
version = "1.0.0"
from(components["release"])
}
}
}
2. 生成产物
生成 iOS XCFramework:
在终端执行:
Bash
ruby
./gradlew :shared:assembleSharedSDKXCFramework
产物路径: shared/build/XCFrameworks/release/SharedSDK.xcframework
生成 Android AAR:
在终端执行:
Bash
ruby
./gradlew :shared:publishMavenAndroidPublicationToLocalRepoRepository
# 或者直接打 release 包
./gradlew :shared:assembleRelease
产物路径: shared/build/outputs/aar/shared-release.aar
第四部分:在现有 App 中集成
拿到产物后,KMP 项目的使命暂时完成,接下来是原生团队的接入工作。
Android 端接入
就像接入任何第三方库一样简单:
Kotlin
scss
// app/build.gradle.kts
dependencies {
// 如果发布到了 Maven 私服
implementation("com.company.kmp:shared-sdk:1.0.0")
// 或者直接引用本地 AAR
implementation(files("libs/shared-release.aar"))
}
iOS 端接入
-
将生成的
SharedSDK.xcframework文件夹拖入 Xcode 工程。 -
在 General -> Frameworks, Libraries, and Embedded Content 中:
- 如果是静态库 (
isStatic = true):选择 Do Not Embed。 - 如果是动态库:选择 Embed & Sign。
- 如果是静态库 (
-
代码调用:
Swift
scssimport SharedSDK // 调用 KMP 中的逻辑 SharedSDK.doSomething() // 如果包含 Compose UI let vc = MainViewController() self.present(vc, animated: true)
结语
通过这种**"组件化 + 二进制分发"**的模式,我们成功将 KMP 技术的引入风险降到了最低。
- 对于原生开发人员: 不需要关心 KMP 的构建细节,只需要像使用普通 SDK 一样使用它。
- 对于架构师: 可以在新业务中尝试 KMP,验证效率提升,而不会影响主 APP 的稳定性。
KMP 不是要取代谁,而是要成为连接 Android 和 iOS 生态的桥梁。现在,就开始增量迁移之旅吧!