用Go语言开发移动应用
速览
本文介绍了使用Go语言开发移动应用的方法和优势。Go语言以其高性能和并发特性,在移动端也有应用潜力。通过跨平台工具,Go可编译为移动应用。这为开发者提供了新的技术选择,有望提升移动应用开发效率。
AI 深度解读
背景
本文作者在过去一年中,使用 Go Mobile 开发了一款名为 Digital Carrot 的跨平台应用,整体体验积极。作者写作此文的目的是总结过程中积累的所有经验,为其他开发者提供参考。Digital Carrot 是一款需要跨平台运行的应用,且需要高度可自定义和可插拔的系统特性。作者选择 Go Mobile,核心原因包括:对 Go 语言的偏爱,以及希望利用 Go 强大的系统库(如 Expr 用于构建表达式,Goja 用于创建 JavaScript 插件)来实现灵活的业务逻辑。
核心内容
选择 Go Mobile 的原因
- 跨平台需求:Digital Carrot 需要同时运行在多个平台,作者希望使用一种能轻松编译并随处运行的方案。
- 丰富的系统库:Go 提供了如 Expr、Goja 等库,能满足应用对可定制性和可插拔性的要求。
如何使用 Go Mobile
Go Mobile 负责 Digital Carrot 的所有业务逻辑。UI 部分起初考虑过 Fyne(Go 的 UI 框架),但评估后认为其不够成熟,最终选择了 Flutter + Go 后端的架构。
整体通信流程:
- Flutter 通过平台通道调用 Swift/Kotlin 原生代码。
- API 调用以二进制编码的 Protobuf 消息形式传递。
- 原生代码将原始二进制数据转发给 Go。
- Go 解码 Protobuf 消息并响应。
- Go 也能通过接口调用原生代码,以访问原生 API。
Flutter 到 Go 的通信
Flutter 与 Go 之间通过 Protobuf 消息 通信。作者强调 Protobuf 是必须的,因为它允许在 Go 和 Dart 中使用结构体/类,而无需手动做大量的 JSON 序列化/反序列化。作者在 Protobuf 中定义消息,然后自动生成 Dart 和 Go 中的对象。由于 Flutter、平台代码和 Go 之间的通信仅支持基本数据类型(字符串、二进制、布尔等),Protobuf 消息非常适合。
在 Flutter 中,定义新的平台通道通常需要在三个地方(Flutter、平台代码、Go)分别实现,非常繁琐。作者因此采用了一个单一函数来处理所有 API 调用:该函数传递一个包含一个大 oneof 块的消息,用以定义具体的 API 调用。Flutter 填充请求部分,放入 CarrotAPI 对象发送给 Go,Go 通过 switch 语句判断 CarrotAPI.api 来调用对应函数并填充响应。这种方式虽然略显笨拙,但比每个函数都创建新平台通道要好得多,避免了在 Swift 和 Kotlin 中重复大量工作。
Go 到 Swift/Kotlin 的通信
这是作者长期感到困难的部分,因为 Go 官方文档对此记录不足。基本思路是:在 Go 中创建一个接口,然后在平台端实现该接口,并在调用 Go 代码时将实现传入。例如,在 Go 中定义接口后,Go Mobile 会生成对应的 Objective-C 或 Kotlin 接口,开发者可以在 Swift 或 Kotlin 中实现,再传回 Go。这些接口同样只支持基本数据类型。由于 Go 到原生代码的调用不多,作者没有为此场景使用 Protobuf。
整体效果评价
好的方面
- 与服务器的无缝集成:Digital Carrot 的同步服务器也用 Go 实现。测试同步时,可以直接将客户端代码导入服务器测试中,无需用 Docker 搭建复杂的测试环境,只需
go test即可在 Mac 上运行,测试在 20 秒内完成。 - 清晰的业务/表现层边界:业务逻辑和 UI 用不同语言编写,自然避免了混用。Flutter 只处理 UI(和少量平台特定逻辑如请求权限),Go 处理所有业务逻辑(与服务器通信、保存数据、验证等)。API 契约测试非常容易:所有业务逻辑都在 Go 中测试,无需启动 UI 组件。此外,还可以进行回放测试:手动操作 UI 并记录 API 调用,然后通过程序回放。UI 测试可以极少,因为业务逻辑非常可靠。这种关注点分离也意味着未来可以轻松替换 UI(例如 iOS 用 SwiftUI,Android 用 Kotlin),考虑到 Google 经常砍项目,这是一个不错的保障——Go 大概率不会消失,而 UI 世界则风云变幻。
- 丰富的库资源:该架构可以同时使用 Go 和 Dart 生态的全部工具。Dart 生态提供了几乎所有与平台权限交互的实用工具,Go 则提供了丰富的系统编程和网络工具。
- 业务逻辑可在任何协议上运行:将通信放在网络能力接口上(如 gRPC),后端可以几乎在任何地方运行。在 Windows 和 Mac 上,后端作为守护进程在后台运行,UI 通过套接字或管道的 gRPC 连接。Digital Carrot 需要持续在后台运行以拦截程序和网站,因此单独运行 Go 守护进程非常高效,无需加载 UI 相关的冗余代码,后台运行时仅需约 30-60MB 内存。虽然对 Digital Carrot 帮助不大,但这对其他类型应用(如文档编辑器)也有优势——可以运行在家庭服务器或本地设备,只需让 UI 指向不同的 API 端点。
- 未来 AI 扩展:所有 API 都具备网络能力,意味着任何客户端(包括 AI 代理)都可以调用。如果未来想添加 AI 功能,无需让 AI 在应用内模拟点击,可以直接指向已有且文档完善的 API。
不好的方面
- 性能开销:每个函数调用都需要对 Protobuf 消息进行序列化和反序列化,增加了几毫秒的额外开销。对于 Digital Carrot 这样的应用完全可接受,但对于游戏等对性能敏感的应用则不可接受。
- 复杂度增加:添加新业务逻辑更困难——需要定义新的 Protobuf 消息并在 Go 中连接。不过这也促使开发者更谨慎地思考新功能。此外,开发者需要掌握至少两门编程语言,并承担额外的认知负担。
关键要点
- Go Mobile 适合跨平台业务逻辑,但 UI 层最好选择更成熟的框架(如 Flutter),而非 Go 原生 UI 库。
- Protobuf 是 Flutter 与 Go 之间通信的关键,避免了手动序列化/反序列化,且天然支持基本数据类型限制。
- 单一平台通道模式比逐个定义平台通道更高效,减少了 Swift 和 Kotlin 中的重复代码。
- Go 到原生代码的通信通过接口实现,但文档较少,需要开发者自行探索。
- 架构优点突出:服务器和客户端代码复用、清晰的分层、丰富的库支持、业务逻辑可在后台稳定运行、易于集成 AI 或更换 UI。
- 性能损失有限(毫秒级),但复杂度增加,适合非实时性、业务逻辑复杂的应用。
- 未来可扩展性强:所有 API 可被 AI Agent 直接调用,无需额外的自动化框架。
意义与影响
本文展示了一种使用 Go Mobile 进行移动端开发的实用架构:以 Flutter 负责 UI,Go 负责全部业务逻辑,通过 Protobuf 和平台通道通信。这种模式虽然增加了初始搭建的复杂度,但带来了显著的长期收益——业务逻辑与 UI 完全解耦、测试简便、后端可独立运行、适配多平台和未来 AI 集成。
对于开发者而言,这种架构特别适合以下场景:
- 应用业务逻辑复杂且需要频繁与后端同步;
- 希望复用服务端 Go 代码进行测试和逻辑处理;
- 需要长期维护、可能面临 UI 框架变迁的项目;
- 对性能不极端敏感,但注重代码健壮性和可扩展性。
同时,它也提示开发者:并非所有场景都适合“同构”或“全栈Go”,适当混合使用不同语言的优势可以更好地平衡开发效率和运行稳定性。Go Mobile 虽然在 UI 层面尚不成熟,但在业务逻辑层仍然是一个可靠的选择。
