Rust 在服务端和嵌入式领域已经有很多跨平台模式的应用案例了,本文主要是来探究一下 Rust 在移动开发领域的跨平台开发模式。
本文中涉及所有信息均来自于互联网,如有错漏,请及时反馈。
目前 Rust 对 iOS 和 Android 平台 Target 都是 Tier 2 和 Tier 3 级别的支持,并且都支持 std 。
对 iOS 的支持:
Tier 2:
Tier 3:
对 Android 的支持:
Tier 2:
看得出来,Rust 对 iOS 和 Android 的支持还算可以。
一些公司使用 Rust 来开发跨平台组件,即,在 iOS 和 Andriod 之间共享代码。
对于 iOS ,Rust 可以直接通过FFi 的方式绑定 C-ABI 来进行交互,可以参考 Mozilla 近期发布的这篇文章来了解如何在 iOS 应用中部署 Rust 库。对于 Android ,Rust 通过 JNI 相关的库来进行相关绑定,Rust 社区生态也有一些类似于 android-ndk-rs的工具来进行 NDK 开发。
可以把这种模式称为:应用的 Rust 后端(App Backend-Rust)。当然,Rust 也可以编译到 WebAssembly ,也可以共享到浏览器端(应用条件比客户端相对严格一些)或桌面端(比如 Electron)。
为什么不用 Cpp 来共享代码?
如果写一次代码到处复用的成本开销超过了其收益,就没有必要使用这种策略了。所以, Dropbox 又回归了使用本地语言进行移动开发,直到现在 2022年。今年3月份 Dropbox 的一篇博客也说明了这一情况:使 Android 的相机上传更快更可靠 。
Dropbox Capture 桌面客户端使用 Rust 开发跨平台组件
虽然在移动领域,Dropbox 放弃了使用 Cpp 共享代码这种策略,但也没有使用 Rust。然而在 2021 年,Dropbox 的另一个团队,Dropbox Capture 则使用 Rust 来构建其桌面客户端的跨平台同步引擎组件。Capture 是一个视觉交流工具,旨在使团队可以轻松地使用屏幕录像、视频消息、屏幕截图或 GIF 异步共享他们的工作。
为什么使用 Rust 呢?
一方面,Rust 在 Dropbox 的产品中应用面越来越广,团队经验提升;另一方面,Rust 本身非常符合 Capture 这个场景,希望更好地控制截屏和录制能力、更好的错误处理以及更快的幕后性能。
使用 Rust 的收益:
根据以上 Dropbox 的实践,可以对 Rust vs Cpp 在跨平台共享组件方面的优劣有一定了解。接下来再看看其他公司的实践。
FullStory 公司 提供了数字体验智能 (DXI) 平台,提供了数据分析服务。该平台也提供了针对移动应用的数据采集功能,其中跨平台组件使用 Rust 开发。
为什么选择 Rust ?
当前的移动生态系统主要由 iOS 和 Android 设备组成。任何想要接触大多数移动用户的人都会发现自己必须开发两个版本的应用程序。
这两个平台存在显着差异:在语言级别上,iOS 应用程序主要是用 Swift(历史上是 ObjC)编写的;另一方面,Android 应用程序主要是用 Java 编写的,如今 Kotlin 越来越受欢迎。因为平台对现代移动应用程序想要做的所有事情都有不同的抽象,从创建按钮小部件到地理定位都不同,所以维护两个不同语言的版本会随着业务发展越来越困难。
FullSotry 公司面临的问题可能更加复杂,因为它们是提供框架给别人去使用,而不是直接发布应用程序。所以要考虑技术选择对客户的影响,例如与他们的代码进行有害交互的可能性,以及在 CPU 和内存使用方面对用户可见的性能影响。
如何使用 Rust ?
首先制定了一个粗略的设计准则:Rust 代码应包含与平台无关的通用代码,特定平台的代码应该保留在特定平台的代码中。
代码组织结构:
对于 iOS ,将 Rust 代码构建为静态库,使用 Ditto 编写脚本来构建 Rust 工具链,使用与 XCode 一致的 LLVM 版本。
对于 Android,将 Rust 构建为静态库,通过 CMake 与一些 C 代码链接,再给 Java 库共享。
使用 Rust 以后的优势
移动开发中使用 Rust 带来的痛苦是什么?
FullStory 工程师在 hacknews 上面透露:
"The only real pain we felt was around iOS bitcode and that was mainly Apple's fault because they make the whole process so Byzantine if you aren't using clang."
"我们唯一真正感到痛苦的是围绕着iOS的位码,这主要是苹果的错,因为如果你不使用clang,他们会使整个过程变得非常复杂。"
1Password 现在也加入了 Rust 基金会。其产品大约 63% 的 1Password 核心代码(加密和同步数据)使用了 Rust 。用 Rust 开发的跨平台组件来支持多个平台,包括移动端和浏览器(将 Rust 编译为 WebAssembly)。但 1Password 把这种方式叫「混合(hybrid)应用程序开发」。
“1Password 也开源了一些跨平台库。比如, sys-locale,轻量级获取位置的跨平台库,支持 iOS/ Android/ MacOS/ Linux/ Windows/ WebAssembly 。还有一个早期开源的 TypeShare库,用于把一些用Rust写的类型生成为其他语言的对应类戏,目前已经不再维护,但是其公司内部还在使用。
1password
首先,1Password 这个 core 库是完全独立的 Rust 库,它定义了明确的 API 来供各个客户端来使用。
op-app和op-ui用于整合其他crates。大部分状态被完全保留在内部,以确保密钥和其他secrets得到正确的处理,同时也使每个客户端UI能够专注于他们的优势,而不是业务逻辑。foundation crate 为 core 提供特定平台的服务(OS Services)。等等。
目前这个通用的 core 库在 Linux 端比较成功,1Password 团队表示会在下一代 1Password 的产品中也采用这种架构。
“1Password 趣事:翻到一篇2019年论文,探讨了流行的密码管理器的安全最佳实践,包括 1Password 。并且同年,在 1P 社区论坛中针对该论文中提到 1P的安全问题引起激烈讨论,1P 成员也第一次提到 Rust 语言,也许这是 1P使用 Rust 的开端,现在1Password 是 Rust 基金会成员。
关于飞书使用 Rust 的公开资料很少,只有2019年字节跳动王枞在QCon分享的《Rust 跨平台客户端开发在字节跳动的实践》,从其中可以了解到,飞书使用 Rust 也是做跨平台组件。飞书客户端非 UI 部分由 Rust 跨平台实现,目前包括移动端和桌面端共 5 个平台。
为什么跨平台使用Rust?
考虑跨平台能带来的收益:
如何做跨平台:
语言选择:
飞书架构:
如图,基本架构是通过一个线程池来管理不同的线程进行交互。
Rust 进程 和 Swift / Java / NodeJS 通过 FFi 和 Protocol Buffers (类似 RPC)的方式来进行调用。
Rust 库的模块组织结构如下:
每个crate都是独立构建、运行、测试和依赖。底层的一些 crate 不会频繁变动。
用到的 Rust 技术栈为:
遇到的问题:
Piccolo 是一款适用于 iPhone、iPad 和(通过Mac Catalyst)macOS 的 Othello(黑白棋) 应用程序,收获了 AppStore 2022年2月精选和2021年最爱游戏荣誉。
AppFlowy是一款开源的笔记类应用,类似于 Notion 。但 AppFlowy 相比Notion更加灵活,用户不仅可以DIY自己的域名、页面外观,还可以跨多个平台使用。
“虽然飞书使用 Rust 开发跨平台组件的细节不得而知,但是据飞书开发成员透露,其应用思路应该和 AppFlowy 是相似的。
AppFlowy 的前端和后端服务都用到了 Rust。这里主要谈前端架构。
前端架构 AppFlowy 采用了领域驱动设计(DDD)的概念,采用分层架构
“DDD,简单来说,就是业务架构映射到了系统架构之上,并能充分隔离业务和技术的边界。一般通过分层架构和六边形架构实现业务与技术实现的隔离。分层架构:遵循“关注点分离”原则来进行上下分层,将属于业务逻辑的关注点放到领域层(Domain Layer)中,而将支撑业务逻辑的技术实现放到基础设施层(Infrastructure Layer)中。另外还有一个应用层(Application Layer),一方面通过暴露业务应用服务的 API 来作为业务的逻辑门面(Facade),另一方面它又是业务和技术实现的粘合剂。六边形架构(端口-适配器):遵循“内外分离”原则来分层。内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施和其他诸如UI/DB 等层,这样更加清晰地勾勒出业务逻辑与技术实现的边界,且将业务逻辑放在了架构的核心位置。体现业务逻辑的应用层与领域层处于六边形架构的内核,并通过内部的六边形边界与基础设施的模块隔离开。在进行软件开发时,只要恪守架构上的六边形边界,就不会让技术实现的复杂度污染到业务逻辑,保证了领域的整洁。如果我们在领域层或应用层抽象了技术实现的接口,再通过依赖注入将控制的方向倒转,业务内核就会变得更加的稳定,不会因为技术选型或其他决策的变化而导致领域代码的修改。
AppFlowy 的前端架构分层了两大部分:
抽象层级由基础设施层到表示层递增,复杂度则相反。
如何跨平台
拿 Flutter 来说,AppFlowy 通过实现 FlowySDK 来做到跨平台:在 Flutter 中定义接口,在 Rust 中实现接口,然后通过 Dart FFi 来绑定 Rust 实现,利用事件分发机制来。
这种模式的优点是:
缺点:
AppFlowy DDD 架构整体业务处理流程
流程如下:
flutter_rust_bridge 的作者 fzyzcjy 也在和 AppFlowy 的开发者沟通,后续 AppFlowy 有可能使用 flutter_rust_bridge,但目前这个进展比较缓慢。
Glean 是 Mozilla 开源的一款现代化跨平台遥测(Telemetry)库。它提供了 Glean SDK支持 Rust/ Kotlin / Swift / Python / JavaScript / QML 等多语言和开发环境。其中 Rust/ Kotlin/ Swift/ Python SDK 是基于一个 Rust core 库来构建的,而 Javascript/ QML 是基于 JavaScript 核心库构建。
“Telemetry(遥测技术),一般是指从物理网元或者虚拟网元上远程实时高速采集数据,实现对网络实时、高速和更精细的监控技术。
跨平台支持:
Glean 使用 uniffi-rs 来自动生成 Rust 库 FFi 绑定,通过需要编写一个接口定义语言(基于WebIDL)文件来描述目标语言可用的方法和数据结构。然后可以生成 Kotlin / Swift 绑定。
除了使用 Rust 开发跨平台共享组件之外,在 Rust 生态中,也有一些框架和游戏引擎来帮助你实现跨平台的应用和游戏。
Tauri 主要是用于开发桌面平台,相比于 Electron 来说,更加轻量、性能更好。Tauri 可以使开发者利用每个平台的 Webview 技术栈,通过 JS Api 调用后台接口。目前支持 Windows/ MacOS/ Linux 等平台,对于 iOS / Android 移动平台的支持正在进行中。
“1Password 8 使用 Electron 开发,引起很多用户的不满。因为 Electron 占用资源过多,也存在内存泄漏问题和安全隐患。用户们考虑到只是一个密码管理软件,不应该占用过多系统资源。也许未来 Tauri 会是一个好的替代。
Tauri 架构
Tauri 主要由两大组件构成:
跨平台支持
tauri-app/tao 利用 Rust 的 trait 和 features 机制,完美实现了跨平台架构:
使用统一的 Window 、Clipboard、 EventLoop 等窗口抽象:
其内部由 platform_impl门面模块来负责调用各个平台的特定实现:
对于 tauri-app/wry来说,跨平台架构的实现也是和 tao 类似的。
建立统一的跨平台抽象:
而内部的 InnerWebView 则是平台特定行为代码,通过 cfg 和 features 来构造一个统一的门面模块,完成跨平台分发。
看得出来,使用 Rust 进行跨平台开发,是非常简洁的。
Bevy 是一个数据驱动的游戏引擎,目前在社区的声望很高,也得到了很多公司的赞助。Bevy 也是跨平台的,在主流桌面操作系统(Linux、macOS、Windows)上开箱即用,无需特殊配置。Bevy 的目标还有 Web 浏览器(通过 WebAssembly,有一定限制)、移动设备(Android 和 iOS,可以构建但不一定能运行)和游戏主机(等待支持)。
跨平台支持
Bevy 本身的抽象程度比较高,跨平台支持主要是依赖于底层的一些库:
这些底层库实现跨平台的模式和上面 Tauri 的架构是相似的。
cargo-mobile 是一个与框架无关的工具,可以生成 Rust 移动项目的所有样板文件,并且包含大量用于在移动设备上构建和运行的便捷命令,可以提升 Rust 在移动领域的开发体验。目前仅支持 macOS 和 Linux 。
ndk-rs, Android NDK 的 Rust 绑定库。该库在 Rust 移动领域生态中应用比较广泛,上面提到到公司产品和项目基本都用到了它。
自 2019 年以来,Android 团队一直致力于将 Rust 编程语言引入 Android 开源项目 (AOSP)中。在 2021 年 4月,谷歌宣布 Android 开源项目(AOSP)现在支持 Rust 编程语言来开发 OS。
关键信息摘录:
IronCore Labs 公司 CEO( Patrick Walsh )去年(2021.10)在其官网发布了一篇 给Apple 的一封公开信:请用 Rust 替换 Objective-C 的文章,内容摘要:
这是一项艰巨的工作,但它会带来巨大的投资回报。迁移到 Rust 可以消除 70% 的漏洞,并且可能更接近 95% 的可远程利用的漏洞。专注于解析来自不受信任来源的数据的库将使Apple平台更加安全。