停止编写重复的代码:使用 Kotlin Multiplatform 共享业务逻辑
Sharing business logic between Android and iOS applications using Kotlin Multiplatform (KMP).

停止编写重复的代码:使用 Kotlin Multiplatform 共享业务逻辑

Advertisement

Here is a blog post written specifically for My Core Pick.


别再把同样的代码写两遍:利用 Kotlin Multiplatform 共享业务逻辑

如果你曾经修复过 Android 版应用的 bug,结果三周后发现同一个 bug 依然在折磨你的 iOS 用户,请举手。

我们都经历过。

这是典型的移动开发困境。你有两个代码库。你有两个团队(或者一个非常疲惫的自由职业者)。你有两种语言。

但你只有一个产品。

多年来,业界的解决方案是“混合(Hybrid)”框架。我们尝试过 PhoneGap。我们与 React Native 搏斗过。我们也曾与 Flutter 暧昧不清。

虽然这些工具很棒,但它们通常要求你在用户界面(UI)上做出妥协。你必须在它们的生态系统中从头开始重建外观和体验。

但是,如果你想保留原生 UI 呢?如果你喜欢 SwiftUI 和 Jetpack Compose 呢?

如果你只想共享应用的“大脑”,而不是“脸面”呢?

这就是 Kotlin Multiplatform (KMP) 登场的时候了。

My Core Pick,我们相信工作要更聪明,而不是更辛苦。今天,我要向你展示为什么 KMP 是共享业务逻辑的未来,以及它如何阻止你重复自己。

“双平台”陷阱

Image

在深入探讨解决方案之前,让我们先谈谈问题所在。

当你为 iOS 和 Android 构建原生应用时,你不只是把 UI 写了两遍。

你把 所有东西 都写了两遍。

你用 Swift 和 Kotlin 分别编写网络层。

你用 CoreData 和 Room 分别编写数据库逻辑。

你在两个不同的文件中分别编写数据验证逻辑(这个电子邮件有效吗?)。

这就是冗余的定义。

这不仅仅是最初编写代码所花费的时间问题。真正的痛苦还在后面。

它来自于维护。

如果你改变了 Android 上的业务规则,你必须记得在 iOS 上也进行更改。如果你不这样做,你的应用行为就会不一致。

用户会注意到这一点的。他们可能不知道原因,但应用感觉“不对劲”。

这种重复增加了出现 bug 的概率范围。它使你的测试需求加倍。它使你的认知负担加倍。

一定有更好的方法来处理逻辑层。

到底什么是 Kotlin Multiplatform?

Image

Kotlin Multiplatform 不是传统意义上的框架。

它不是那种在屏幕上渲染画布的“一次编写,到处运行”的黑盒。

KMP 是一个设计用于在平台间共享代码的 SDK。

它将 Kotlin 代码编译成用于 Android 的 JVM 字节码。这是简单的部分。

但神奇之处在于,它将 同样的 Kotlin 代码编译成用于 iOS 的 LLVM 字节码。这意味着它在 Apple 硬件上原生运行。

对于 Xcode 来说,你共享的 Kotlin 代码看起来就像是一个 Objective-C 框架。

你可以像使用任何其他原生库一样导入它、调用函数和读取数据。

这里的理念很简单:共享逻辑,原生 UI。

你保留你漂亮的 SwiftUI 视图。你保留你高性能的 Jetpack Compose 布局。

你只是不再把驱动它们的那些代码写两遍而已。

你实际上应该共享什么?

Image

当我开始使用 KMP 时,我试图共享所有东西。

那是一个错误。

KMP 的威力来自于知道在哪里划清界限。你不应该试图共享按钮、动画或导航转换。

你应该共享业务逻辑。

那么,这在实践中是什么样子的呢?以下是我们关注的三个主要支柱。

1. 数据层 (The Data Layer)

这是最容易实现的目标。

每个应用都需要与互联网对话。每个应用都需要解析 JSON。

在传统的设置中,你有 Swift 的 Codable 结构体和 Kotlin 的 Data Classes。如果后端发生了变化,你必须更新两者。

使用 KMP,你只需编写一次数据模型。

你使用像 Ktor 这样的库进行网络连接,使用 Kotlin Serialization 进行解析。

你编写一个仓库(Repository)类来获取数据、解析数据并处理错误。

你的 iOS 和 Android 应用只需调用 repository.getData()。它们不关心数据是如何到达那里的。它们只是显示它。

2. 本地存储 (Local Storage)

数据库管理是复杂的。

在两个不同的平台上管理迁移是一场噩梦。

使用 KMP,我们使用像 SQLDelightRoom(现在支持 KMP)这样的库。

你在一个地方定义数据库模式。你在一个地方编写 SQL 查询。

生成的代码会处理特定于平台的驱动程序。

你的 Android 应用使用 SQLite 驱动程序。你的 iOS 应用使用 Native 驱动程序。

管理你如何保存、检索和删除用户数据的逻辑在两个平台上保持完全相同。

3. “大脑” (ViewModels)

这是事情变得真正令人兴奋的地方。

实际上你可以共享你的 ViewModels。

ViewModel 是保存屏幕状态的组件。它决定当用户点击按钮时会发生什么。它决定当网络失败时显示什么。

这是纯逻辑。它与像素无关。

通过共享 ViewModel,你的 UI 变得非常“傻瓜化”——这其实是一件好事。

你的 SwiftUI 视图只是观察来自共享 Kotlin 代码的状态对象。

如果状态说 isLoading = true,UI 就显示一个旋转加载器。

UI 不需要知道 为什么 正在加载。它只是服从命令。

它是如何工作的:技术一瞥

我知道你在想什么。

“这听起来不错,但配置起来会不会是场噩梦?”

以前确实是。但在过去的一年里,生态系统已经大大成熟了。

让我们看看项目结构。

源集 (The Source Sets)

一个 KMP 项目被分为几个“源集(Source Sets)”。

首先,你有 commonMain

这里是你 90% 代码的栖息地。这是纯 Kotlin。它包含你的模型、仓库和逻辑。

然后,你有 androidMainiosMain

这些文件夹用于存放特定于平台的代码。

如果你在编写纯算法,它放在 commonMain 中。

但有时,你需要访问特定的设备功能,比如相机或蓝牙。

Kotlin 默认不知道如何访问 iOS 相机。

Expect/Actual 机制

这是共享世界和原生世界之间的桥梁。

它的作用就像一个接口(Interface),但是在语言层面上。

在你的 commonMain 文件夹中,你可能会写:

expect fun getPlatformName(): String

编译器现在会抱怨。它会告诉你,你承诺了一个函数,但没有提供实现。

所以,在 androidMain 中,你写:

actual fun getPlatformName(): String = "Android"

而在 iosMain 中,你写:

actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()

注意,在 iosMain 中,我可以访问 UIDevice。这是一个 Apple API。

我可以直接在 Kotlin 内部访问它,因为 KMP 可以访问 iOS SDK。

这种机制允许你在共享高级逻辑的同时,在需要时深入使用原生功能。

现实世界的收益

我们在 My Core Pick 最近的一个项目中实施了 KMP。

结果改变了我对移动开发的看法。

一致性为王

最大的好处不是速度。而是一致性。

我们在应用中有一个复杂的“健康评分”计算。

过去,iOS 开发人员会以一种方式解释需求,而 Android 开发人员会以另一种方式解释。对于相同的数据,我们最终会得到略微不同的分数。

使用 KMP,我们只编写了一次算法。

我们在 Kotlin 中对它进行了大量的单元测试。

我们部署了它。

我们要确信,如果用户切换设备,他们的分数将完全相同。

更快的功能迭代

一旦架构搭建完成,添加新功能就变得快多了。

当我们需要添加一个新的 API 端点时,我们在共享模块中添加它。

突然之间,iOS 和 Android 开发人员都能立即访问它。

我们不必等待 iOS 开发人员在网络层上赶上进度。

这创造了一种“接力赛”的动态。核心逻辑开发人员清理道路,UI 开发人员冲向终点线。

增强的测试

测试 UI 是困难的。测试业务逻辑(相对)容易。

因为我们的逻辑与操作系统分离,我们可以直接在 JVM 上运行测试。

这非常快。我们不需要启动 Android 模拟器或 iOS 模拟器来测试我们的 ViewModels。

我们编写标准的 JUnit 测试。它们在几毫秒内就能运行完毕。

这鼓励我们编写更多的测试,从而产生更稳定的产品。

它准备好迎接黄金时段了吗?

你可能对采用新技术犹豫不决。

这是一种健康的怀疑态度。

然而,KMP 已被巨头们使用。

Netflix 在用。VMWare 在用。麦当劳在用。

Google 官方支持它。

库的生态系统正在蓬勃发展。

对于依赖注入,我们有 Koin

对于网络,我们有 Ktor

对于日志记录,我们有 Napier

工具就在那里。支持就在那里。

结论

我们正处于移动开发的新时代。

严格隔离 iOS 和 Android 逻辑的日子正在结束。那样做不仅成本太高,而且太耗时。

但我们也不愿意牺牲原生 UI 的质量。

Kotlin Multiplatform 击中了最佳平衡点。

它在重要的地方(UI)尊重平台差异,并在关键的地方(逻辑)统一平台共性。

别再把同样的代码写两遍了。

别再把同一个 bug 修复两遍了。

开始共享你的业务逻辑吧。你的代码库(以及你的精神健康)会感谢你的。


喜欢这篇深度文章吗?欢迎在 My Core Pick 上查看更多技术见解。

🔥 Share this Insight

𝕏 Post
Sharing business logic between Android and iOS applications using Kotlin Multiplatform (KMP).

停止编写重复的代码:使用 Kotlin Multiplatform 共享业务逻辑

Here is a blog post written specifically for **My Core Pick**. *** # 别再把同样的代码写两遍:利用 Kotlin Multipl...

My Core Pick.
mycorepick.com

Advertisement

Back to Posts