我最早接触Kotlin Multiplatform(以下简称KMP)是在2022年底,当时第一个反应是:这不就是Flutter换个皮嘛,学它干吗。真正把一个共享模块同时跑进Android和iOS两个工程之后,我才意识到这个判断错得离谱——KMP要解决的根本不是UI复用,而是业务逻辑层的多端一致问题。如果你正在评估跨平台方案,或者已经在用KMP但没完全搞清楚代码该怎么切分,这篇文章把我的理解、实操过程和踩过的坑一次说清楚。内容覆盖框架定位、编译原理、共享边界、项目创建、存量改造和版本配套,适合想认真评估KMP或准备动手落地的同学。
1. KMP到底解决什么问题:先把这个框架的错误预期扭过来
1.1 KMP和Flutter、RN的本质区别在"共享层"
很多人一听到"跨平台"就默认等于"一套代码到处跑",但KMP的目标不是这个。Flutter和React Native走的是"UI和业务逻辑一起复用"的路线,你写一套Dart或者JavaScript代码,渲染层由各自引擎接管。KMP的思路完全反过来:UI层不共享,Android端用Compose或原生View,iOS端用SwiftUI或UIKit,只把业务逻辑、数据层、领域模型这些"无界面代码"放进共享模块。
这个区别在选型上非常重要。如果你的产品是强UI型的,比如工具类App、信息流产品,Flutter那种"一套UI全部复用"的优势很明显。但如果你的产品两个端已经有大量原生交互、系统能力接入、自定义视觉效果,硬套Flutter可能比原生开发更痛苦。KMP不碰你的UI层,它只是在数据层和逻辑层介入,这种侵入性极低的跨平台方案,对大中型原生团队其实友好得多。
还有一点很多人没意识到:KMP是JetBrains和Google共同背书的技术路线,Android的官方文档已经直接支持KMP共享模块嵌入到原生App中。这一点对Android团队来说意义很大,因为这意味着它是"原生开发的延伸",而不是"另一套生态"。
1.2 别把KMP当KMP算法:一个高频搜索误区
搜索KMP相关资料的时候,你会看到大量"KMP算法""KMP算法next数组计算""KMP算法属于动态规划吗"的帖子。这里的KMP是Knuth-Morris-Pratt字符串匹配算法,和Kotlin Multiplatform没有任何关系,只是恰好缩写撞了。
我记得有次在技术群里有人问"KMP项目创建"的步骤,底下有人回答用next数组推导复杂度……场面一度很混乱。所以你要先确认自己搜到的是不是"Kotlin Multiplatform"的社区资料,一般可以加"Kotlin"或"Gradle"关键词锁定范围,比如搜"KMP Gradle配置""KMP expect actual"这类带上下文的关键词。
1.3 KMP适合谁,不适合谁
按我这个阶段的观察,适合上KMP的场景有这几个:
- 团队同时维护Android和iOS,两端有不少重复的领域逻辑和网络层代码
- 业务规则复杂且对一致性要求高,比如支付计算、权限判断、埋点逻辑
- 想做逻辑复用但不想被UI框架绑死,希望保留原生体验
- 团队里至少有一方熟悉Kotlin,因为共享代码用Kotlin写,Android端几乎零学习成本
不适合的情况也很明显:一个纯iOS团队要上KMP就得先补Kotlin;小型项目或原型验证类项目,KMP的前期工程复杂度不划算;如果你连稳定的数据层都还没定下来,也别急着共享。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. shared模块的一次编译多层落地:理解编译目标和expect/actual
2.1 三个sourceSet与双端编译产物
KMP工程的核心是一个共享模块,通常叫shared,它通过Gradle配置多个编译目标。我第一次打开KMP模板的build.gradle.kts时,看到androidTarget() iosArm64() iosSimulatorArm64()这些配置,第一反应是"这不是一个模块吗?怎么像生成了好几个项目"。
实际上KMP确实会把共享代码编译成多个产物:Android端编译成JVM字节码,变成AAR依赖;iOS端通过Kotlin/Native编译成LLVM中间表示,再打包成framework导入Xcode。同一份Kotlin代码,在不同平台上走的编译链路完全不同,这是KMP最核心的底层机制。
代码层级上,KMP把所有源码分成三个sourceSet:
commonMain:放两端共享的代码,是代码量最多的地方androidMain:放Android平台专属实现,比如OkHttp引擎、Android权限APIiosMain:放iOS平台专属实现,比如Darwin网络引擎、UIKit调用
在Android Studio里,你看到的依赖关系和源码目录是这样的:
kotlin复制kotlin {
androidTarge
