1. 项目概述:Kotlin Multiplatform (KMP) 是什么?
如果你是一名移动端开发者,尤其是做Android的,这两年肯定没少听人提起“KMP”或者“Kotlin Multiplatform”。乍一听,这又是一个跨平台框架?跟Flutter、React Native抢饭碗的?其实不然。KMP的定位非常独特,它更像是一个“代码共享策略”而非一个“UI框架”。它的核心思想是: 让你用Kotlin这门语言,只写一次业务逻辑和数据层代码,然后让这套代码能直接运行在Android、iOS、桌面、Web甚至服务器端。
简单来说,它解决的是跨平台开发中最头疼的问题之一: 逻辑不一致 。想象一下,你的App在Android和iOS上,登录验证的逻辑、网络请求的数据解析、本地数据库的模型定义,是不是得写两套?用Java/Kotlin写一遍,再用Swift写一遍。不仅开发周期翻倍,后期维护、修复Bug更是噩梦,稍有不慎两边就不同步了。KMP就是来终结这个噩梦的。它让你用Kotlin写一套共通的逻辑,然后通过编译器,分别生成对应平台的本地代码(在Android上是JVM字节码或机器码,在iOS上是Objective-C或Swift接口和实现),最终由各平台的原生UI去调用。这意味着,你的iOS App依然是用SwiftUI或UIKit构建的,体验是100%原生的;你的Android App也依然是原生的Jetpack Compose或View体系。只是它们背后那些“脏活累活”,都由同一套Kotlin代码来干了。
所以,KMP不是要取代Flutter(用Dart画UI)或React Native(用JavaScript+React),它走的是另一条路: “共享逻辑,原生UI” 。这对于那些对UI体验有极致要求,但又苦于双端开发成本太高的团队来说,吸引力是巨大的。从搜索到的资料看,麦当劳、索尼、Google Workspace、B站这些大厂都在用,而且效果显著——减少了崩溃、提升了性能,更重要的是把Android和iOS团队从“各自为战”变成了“统一移动团队”。这背后的价值,远不止省了几行代码那么简单。
2. KMP的核心设计思路与架构解析
2.1 “共享什么”与“不共享什么”的哲学
KMP的设计非常务实,它不强求你“All in”。相反,它提供了灵活的共享层级,你可以根据项目阶段和需求来选择:
- 共享纯逻辑(最大公约数) :这是最常见的起点。将网络请求、数据模型(Data Class)、业务规则(如计算、验证)、本地存储(使用SQLDelight等KMP库)等与UI无关的代码抽离出来,放在一个
commonMain的源代码集中。这部分代码用纯Kotlin编写,不依赖任何平台特定API。 - 共享UI逻辑,但使用原生UI组件 :比如,你共享了ViewModel(通过
kotlinx.coroutines和kotlinx.serialization),它持有状态和业务逻辑。在Android端,你用Jetpack Compose的viewModel()去获取;在iOS端,KMP会生成一个Swift类,你可以在SwiftUI的@StateObject或UIKit的ViewController中去实例化和观察它。UI渲染还是原生的,但驱动UI的状态和逻辑是共享的。 - 共享整个UI(使用Compose Multiplatform) :这是更激进但也更彻底的方案。JetBrains推出了Compose Multiplatform,它允许你用声明式的方式编写UI,这套UI代码可以编译到Android、iOS、桌面和Web。这时,你共享的就不只是逻辑,而是整个界面了。但这并不意味着你做出了一个“非原生”的App,因为Compose Multiplatform在设计上会尽量遵循各平台的设计规范(Material Design on Android, Cupertino on iOS),并且性能是原生的。
KMP鼓励你从第1种或第2种方式开始,逐步演进。这种“渐进式采用”策略大大降低了迁移风险和初始成本。你不需要重写整个App,可以先挑一个独立的、逻辑复杂的模块(比如用户认证、支付流程、数据同步引擎)用KMP实现,然后像使用一个普通库一样,分别集成到现有的Android和iOS工程里。
2.2 关键技术原理:预期声明与实际实现
KMP实现跨平台的核心机制,在于Kotlin语言层面的两个关键字: expect 和 actual 。这是理解KMP如何工作的钥匙。
-
expect(预期声明) :在公共代码模块(commonMain)中,你声明一个函数、类、属性或接口,但你只定义它的“契约”(名字、参数、返回类型),而不提供具体实现。因为你不知道在Android上该怎么做,在iOS上又该怎么做。例如,你需要获取设备的唯一ID,这在各平台API完全不同。// 在 commonMain 中 expect fun getDeviceId(): String这相当于立下了一个规矩:“所有平台,都必须提供一个叫
getDeviceId、返回String的函数。” -
actual(实际实现) :在各个平台特定的源代码集(如androidMain、iosMain)中,你需要为每一个expect声明提供一个具体的、使用该平台原生API的实现。// 在 androidMain 中 import android.provider.Settings.Secure actual fun getDeviceId(): String { // 使用Android API获取Android ID return Secure.getString(context.contentResolver, Secure.ANDROID_ID) }// 在 iosMain 中 (通过Kotlin/Native) import platform.UIKit.UIDevice actual fun getDeviceId(): String? { // 使用iOS API获取UUID return UIDevice.currentDevice.identifierForVendor?.UUIDString }
在编译时,KMP的编译器(Kotlin/Native for iOS, Kotlin/JVM for Android)会分别将 commonMain 中的 expect 部分与对应平台的 actual 实现链接起来,生成最终的平台本地库( .framework for iOS, .jar or .aar for Android)。
实操心得 :刚开始用
expect/actual会觉得有点繁琐,但它强制你清晰地思考“哪些是平台无关的抽象逻辑,哪些是平台相关的具体操作”。这本身就是一种良好的架构设计训练。一个常见的技巧是,尽量把expect声明得“高”一些,即参数和返回值尽量使用通用的数据模型(如你自己的Data Class),而不是平台特定类型(如Android的Context)。这样actual实现里虽然用了原生API,但对外接口是统一的,公共逻辑代码会非常干净。
2.3 与Compose Multiplatform的关系
这是最容易混淆的点。从搜索资料中JetBrains官方的解释可以明确:
- Kotlin Multiplatform (KMP) 是底层技术,负责 代码共享 ,主要是业务逻辑、数据、网络等。
- Compose Multiplatform 是建立在KMP之上的一个 UI框架 ,负责 UI共享 。
你可以只用KMP来共享逻辑,UI完全用原生技术栈(SwiftUI/Jetpack Compose)。你也可以采用Compose Multiplatform,连UI也共享了。后者提供了更极致的代码复用率,但要求你的团队接受Comp



被折叠的 条评论
为什么被折叠?



