
引言
"开发一致性很关键"------这句话在分布式系统领域经常被提及,在移动和 Web 应用开发中同样适用。
在当今这个单个应用需要在多个平台上无缝运行的时代,确保各平台之间的一致性不仅是一个目标,更是一种必要。多年来,开发者们一直面临着艰难的抉择:是为每个平台分别构建原生应用,还是采用跨平台框架。
从传统意义上来说,针对 Android 和 iOS 进行原生开发需要将资源翻倍投入:两套代码库、两支工程团队,还需要付出双倍的努力来确保功能和业务逻辑同步。当应用程序需要一致的 UI 和相同的业务逻辑时,自然就需要共享驱动它们运行的代码。
这种需求催生了像 Flutter 和 React Native 这样的跨平台工具。虽然这些工具非常有效,但往往需要做出重大的权衡。它们通常要求开发者使用不同的 UI 渲染引擎(Flutter )或使用桥接(React Native),与原生代码相比,这势必会导致性能损失。
在这种情况下,KMP (Kotlin Multiplatform) 提供了一种独特的方法。KMP 并不是取代原生平台的 SDK,而是与之协同工作,使开发者能够共享通用代码,并能以直观的惯用方式访问平台 API。具体来说,这意味着你可以像使用 React Native 一样,通过使用原生 UI 工具包来创建一款保留各平台外观和感觉的 KMP 应用程序。你也可以采用 CMP (Compose Multiplatform),效仿 Flutter 的方式共享 UI 实现,尽管这可能会导致应用在 iOS 上失去 "原生" 外观。
总之,当与 CMP 结合使用时,KMP 提供了一个极具吸引力的应用场景:与 Flutter 类似,它能编译成本地代码以实现出色的性能,同时也保留了使用原生 UI 工具包的选项,这一点与 React Native 一样具有灵活性。
KMP 正成为跨平台开发的一个强大替代方案,它提供了一种在不牺牲原生应用性能和体验的前提下共享代码的途径。作为一种替代方案,KMP 也有自身需要权衡的方面,本文将深入探讨这些问题。
虽然本文主要关注 Android 和 iOS 开发,但 KMP 的应用前景更为广阔。借助同样的共享逻辑,它可用于构建桌面应用(Windows、macOS、Linux)、网页应用(通过 Kotlin/JavaScript),甚至服务器端应用。
什么是 Kotlin 跨平台(KMP)
KMP 允许开发团队在不同平台间编写和共享代码,而且借助 CMP ,仍可选择构建原生 UI。它并非那种将平台特性完全抽象掉的 "非此即彼" 的框架。相反,它是一个精妙的工具,能让你共享应用中与平台无关的部分,同时让特定于平台的部分保持原生特性。Android 开发者可以用 Kotlin 编写 UI 代码,iOS 开发者则可以用 Objective-C 或 Swift 编写 UI 代码。但这并不意味着只能使用原生界面构建 UI,这是一个重要的区别。KMP 为你提供了按自己意愿构建 UI 的灵活性。
KMP 是如何工作的
KMP 有特定的项目结构以及 expect/actual 机制,这使其能够跨平台运行。一个典型的 KMP 项目分为多个模块:
commonMain包含通用代码。这段代码只需编译一次,即可在所有目标平台上共享。你可以将业务逻辑、网络调用等放在这个模块中。androidMain包含 Android 实现。Android 设备的 UI 可以在这个模块中用 Kotlin 编写。iosMain包含 iOS 实现。iOS 设备的 UI 可以用 Swift 或 Objective-C 编写。
expect 和 actual 关键字是通用实现与特定平台实现之间的桥梁。在 commonMain 中,你可以预期某个类或函数存在,并定义其 API。然后,在 androidMain 和 iosMain 中,为该声明提供特定于平台的实际实现。例如,在 commonMain 中你可能预期有一个生成唯一标识符(UUID)的函数,然后在 androidMain 中使用 Android 的 UUID.randomUUID() ,在 iosMain 中使用 iOS 的 NSUUID().uuidString 来提供实际实现。
利用上述结构,可以为跨平台应用同时进行构建,而不会牺牲应用性能。
KMP 的优势
降低成本
统一架构并使同一批开发者能够为多个平台编写代码,无疑能显著降低工程成本。通过将业务逻辑、网络等功能置于共享的 commonMain 模块中,就无需针对同一功能在不同平台重复实现和测试。这直接避免了重复工作,也减少了特定于平台的错误,从而打造出更强大、更可靠的产品。此外,由于核心逻辑只需测试一次,这也简化了质量保证流程。需要重点指出的是,Flutter 和 React Native 也具备这一优势。在这方面,KMP 并不比其他技术更具有优势。
原生性能
与其他跨平台工具不同,KMP 允许开发者使用平台的原生工具构建 UI。共享的 Kotlin 代码会被编译成目标平台所需的格式,为 Android 生成 JVM 字节码,为 iOS 生成原生二进制文件。与 React Native 不同,运行时不存在解释层,这使得应用程序能够实现接近原生的性能。
逐步采用
Flutter (使用 Dart )和 React Native (使用 TypeScript 或 JavaScript )需要 "全有或全无" 式的采用方式,而 Kotlin 多平台则并非如此。它可以在代码库中逐步引入,过渡会更加平缓。你可以将 KMP 逐步引入到现有的原生应用中,这有助于确保应用在整个过程中保持稳定。常见的起点是将单个功能或特定层迁移到共享的 KMP 模块中。
平滑演进
由于谷歌正式认可 Kotlin 作为安卓开发的主要语言,大多数安卓开发团队都已在使用 Kotlin 进行开发。对于这些团队来说,向 Kotlin 多平台的过渡门槛极低。
Flutter vs React Native vs KMP

虽然这三项技术都支持跨平台开发,但它们的工作原理不同,特别是在 UI 和整体理念方面。下表总结了我们上面讨论的主要差异:
| 特性 | Flutter | React Native | KMP |
|---|---|---|---|
| 语言 | Dart | JavaScript / TypeScript | Kotlin |
| UI 渲染 | 使用 Skia 引擎渲染自己的 UI | 通过 JS 桥控制原生 UI 组件 | 可以使用原生 UI (SwiftUI/Compose ) 或共享 UI (Compose Multiplatform) |
| 性能 | 优秀,编译为原生代码,无需桥接 | 良好,但 JavaScript 桥可能会成为瓶颈 | 优秀,编译为平台原生格式,无需桥接 |
| 采用 | 完全投入 | 完全投入 | 逐步采用 |
iOS 系统中的现状
历史上,Kotlin 从未用于开发 iOS 应用程序。但有了 KMP 和 CMP,现在这已经成为可能。
长期以来,Kotlin 多平台在 iOS 方面秉持的理念是 "共享逻辑,原生构建用户界面"。虽说这是一种强大的方法,但它仍意味着用户界面开发是特定于平台的任务。随着 1.8.0 稳定版的发布,这种情况发生了改变。截至 2025 年 5 月 6 日,用于 iOS 的 CMP 达到一个稳定的、可用于生产环境的里程碑。
这一里程碑对于 Kotlin 多平台生态系统而言是一项重大成果。Jetpack Compose 是谷歌为安卓系统打造的现代声明式用户界面工具包。有了 CMP ,现在你可以使用完全相同的 Kotlin 代码为 Android 和 iOS 定义 UI。这意味着不仅业务逻辑可以共享,用户界面本身也能共享。目前,该框架在功能上已经与面向 iOS 的 Jetpack Compose 不相上下,这使其成为新项目的可行选择。在我看来,CMP 1.8.0 版本不仅对 iOS 开发有价值,而且实现了 KMP 真正的价值主张,因为开发者现在能够基于此构建整个应用程序。
此外,某些屏幕使用原生 Swift/UIKit ,而其他屏幕使用 Compose 进行混合搭配的选项提供了极大的灵活性。为此,你可以依赖框架提供的特定包装组件,这些组件利用了 SwiftUI/UIKit 的互操作性。例如,要在原生 SwiftUI 应用中使用 CMP 界面,可将可组合 UI 包装在 ComposeUIViewController 中。然后,在 SwiftUI 代码中,为 UIViewControllerRepresentable 协议创建一个结构体,该结构体将管理控制器。若要在 Compose UI 中使用原生 SwiftUI 界面,可将 SwiftUI 视图托管在 UIHostingController 中,该控制器可使用 UIKitView 可组合项直接嵌入到 Compose 布局中。
使用 KMP 的大用户
KMP 并非只是一个理论概念,像网飞(Netflix)、麦当劳(McDonald's)和飞利浦(Philips)这样的全球大型公司都在实际产品中使用它。
网飞
网飞是 KMP 的早期重要用户,自 2020 年 10 月就开始采用该技术。他们利用 KMP 统一了各应用程序中的业务逻辑,确保无论用户使用何种设备,各项功能的运行表现一致。他们还在内部的 Prodicle 应用程序中使用了该技术,该应用程序为电视和电影项目的实际制作提供支持。
我们的 Android 和 iOS 应用程序有着共享的架构,两个平台上编写的业务逻辑相似,甚至在某些情况下完全相同。------ 网飞
网飞能够实现代码与底层平台 50% 的解耦。展望未来,网飞还谈到了将其应用程序发展成为一个薄 UI 层的可能性,该 UI 层共享用 KMP 编写的相同业务逻辑。
麦当劳
麦当劳在其应用程序中引入应用内支付功能时,开始使用 KMP (早期版本中称为 KMM )。在验证了这种方法后,他们将 KMP 的使用扩展到其他服务。
将 KMM 集成到应用程序流程中,使我们能够在一个地方开发解析和存储逻辑,此外还能开发在每个平台上正确显示本地化数据和产品所需的业务逻辑。------ 麦当劳
例如,为了满足不同国际地区客户的各种需求,他们将管理特定地区产品和数据的逻辑集中到一个共享的 KMP 模块中。在这个模块里,他们将 KMP 引入到处理特定地区和设备的产品与数据的业务逻辑中。
最终,麦当劳重写了整个应用程序以采用 KMP。通过这一战略转变,他们将独立的 Android 和 iOS 开发团队合并成一个更加统一且高效的移动开发团队。
飞利浦
2018 年,飞利浦采用 KMP 来摆脱原生开发。飞利浦利用 KMP 为其互联消费产品和个人健康产品开发了一个用于设备通信和数据同步的共享 SDK,实现了牙刷和空气净化器等设备与移动应用的连接。通过使用 KMP,他们得以保留共享的业务逻辑。
蓝牙低功耗接口存在很大差异,以至于无法设计出一种能同时适配两者的方案。所以在这方面,我们仍将采用原生开发方式。------ 飞利浦
他们最初在处理蓝牙低功耗的蓝牙 API 时遇到了一些问题。为了解决这个问题,他们对蓝牙协议栈保留了原生实现,这表明开发人员并非在整个代码库中都采用 KMP。
我们可以看到,现在越来越多的公司正在采用 KMP,它开始受到重视。各公司一开始行动较为缓慢,但随着他们开始看到其好处,便逐渐增加了使用案例。我建议大家查看所有这些案例研究,因为我仅提及了少数几个。
挑战与考量

虽然 KMP 为跨平台开发提供了一种很有前景的方法,但我们也必须认识到 KMP 目前的局限性。
生态系统成熟度
与现有的工具(如 Flutter 和 React Native )相比,一个主要挑战在于 KMP 的开发者社区规模较小,支持生态系统也不够完善。虽然核心功能得到了很好的支持,但要找到满足特定需求(比如高级蓝牙交互或后台服务)的、适配 KMP 的库,情况依然不容乐观,就如同我们在飞利浦案例中看到的,可能仍需编写特定平台的代码实现。这有时意味着开发者需要围绕原生解决方案创建自己的包装器,这并不是理想的情况 。
iOS 团队的学习曲线
对于 iOS 团队而言,采用 Kotlin 和 Gradle 构建系统可能会有一定的学习曲线,它们在安卓生态系统中很常见,但对大多数 iOS 开发者来说是新鲜事物。
Kotlin/Native 互操作性
Kotlin/Native 与 Swift/Objective-C 之间的互操作性存在自身的局限性。Kotlin/Native 通过 Swift/Objective-C 提供间接互操作性。例如,一些 Kotlin 语言特性在 Objective-C 中可能没有直接对应的特性,这就要求开发者在开发过程中了解这些差异。这一要求可能会成为一个很大的障碍,因为大多数开发者在开始开发之前并不了解这一点。泛型就是一个例子,在转换过程中类型信息会丢失。开发者需要留意传递的类型,并围绕此进行检查。
工具和开发流程
虽然相关工具在不断改进,但目前仍不如原生开发工具那样无缝衔接。KMP 有一些插件,使用起来更加便捷,而且 JetBrains 正在积极开发这些插件。
虽然面临的挑战不少且不容忽视,但这种情况正在迅速改变。可以说,对 Kotlin 本身的支持已经成熟,并且还在不断发展,而 KMP 社区正是这一趋势的直接受益者。随着采用率的提高,共享知识和开源库的数量也会增加。
总结
跨平台开发一直是在权衡利弊中进行的:要在开发效率与原生性能以及外观和用户体验之间找到平衡 。有了 KMP,这些权衡因素在一定程度上有所减少。你可以获得共享代码库带来的效率,同时交付几乎与原生应用性能相当的产品。
虽然核心功能有良好的支持,但要找到满足特定需求(如高级蓝牙低功耗交互或后台定位服务)的现成 KMP 库,可能仍需要编写特定于平台的实现代码。这确实是个问题,因为需要所有这些功能的应用程序无法仅通过 KMP 构建。但正如前面提到的,这是一个迭代演进的过程,由于 KMP 开发活跃,更好的支持将会推出。
推荐一个网站,可以搜索当前的 KMP 相关的库:klibs.io。
KMP 提供了一个灵活且有前景的前进方向,使团队能够为多平台世界构建一致的高性能应用程序,同时又不脱离原生基础。对 Kotlin 的支持力度很大,其多平台功能也只会从此不断发展壮大。