来源: Cross platform native mobile development with Rust (sandigital.uk)
Cross platform native mobile development with Rust
使用 Rust 进行跨平台原生移动开发
San Digital have extensive experience of mobile development and the use of Android as an embedded operating system. We treated android as a deployment target target for Rust firmware as well as writing our intricate real time communications component for both iOS and Android. This approach has advantages, you can maintain a single code base for a complicated communications layer, while also taking advantage of the full native capabilities of each platform.
San Digital 拥有丰富的移动开发经验和使用 Android 作为嵌入式操作系统的经验。我们将 Android 作为 Rust 固件的部署目标,同时也为 iOS 和 Android 编写复杂的实时通信组件。这种方法有很多优点,您可以为复杂的通信层维护单一的代码库,同时还可以利用每个平台的完全原生功能。
While building our Smart Home / Smart Energy platform for Presciense, we wanted to differentiate ourselves from the majority of products on the market that suffered from high latencies. Even while connected to the same network as many IoT gateways at the time, a simple operation like turning a light on and off could take multiple seconds to both perform and display on the application. The biggest contributor to these latencies (and also availability problems at scale) was the use of centralised, rather than peer to peer communications between the mobile handset and gateway. Along with this, most competitors used synchronous http or polling tricks to act on a device and receive data on its state. We came up with design utilising multiple factors to achieve low latency and reduce overall operational costs at high scales.
在为 Presciense 打造我们的智能家居/智能能源平台时,我们希望将自己与市场上大多数受到高延迟影响的产品区分开来。即使同时与许多物联网网关连接到同一网络,像开灯和关灯这样的简单操作也可能需要数秒才能在应用程序上执行和显示。造成这些延迟(以及大规模可用性问题)的最大原因是在移动手持设备和网关之间使用集中通信,而不是点对点通信。与此同时,大多数竞争对手使用同步 http 或轮询技巧来操作设备并接收其状态的数据。我们提出了利用多种因素来实现低延迟并在高规模下降低整体运营成本的设计。
Secure, zero password p2p communications while on the same network as the gateway
Everything asynchronous, from invocations to state updates
Aggressive approach to packet sizes and overall data use
安全,在同一网关网络下的零密码 p2p 通信
从调用到状态更新,一切都是异步的
优秀的数据包大小和整体数据使用
Achieving these features and NFRs set over 2 mobile platforms (along with our linux based command line tools for testing) is a challenge for a small team, as to do so we needed:
对于一个小型团队来说,要在两个移动平台上实现这些功能和 NFR(以及基于Linux的命令行工具进行测试)是一个挑战,为此,我们需要:
Gateway discovery, LAN and internet connectivity
TLS 1.3, as it solved security issues that made our local PKI much simpler, but was not yet available on mobile platforms
Highly efficient binary protocol using Captain Protocol and shared dictionaries
Real time messaging utilising operational transformations within 50ms time windows to combine outgoing commands and state changes into their simplest possible representations
A memory efficient DOM, updated asynchronously as devices connected to the gateway changed state
网关发现,局域网和互联网连接
TLS 1.3,它解决了安全问题使我们本地 PKI 更简单,但在移动平台上还不可用
使用船长协议和共享字典的高效二进制协议
利用50毫秒时间窗口内的操作转换将传出命令和状态更改组合成最简单的可能表示的实时消息传递
一个内存效率高的 DOM,当连接到网关的设备状态改变时异步更新
A component this intricate would be difficult to both develop, test and maintain over the 3 required platforms. Having this client functionality as a cross platform component that could also be tested on a non mobile platform independently of a user interface seemed to have a number of advantages, as did developing a client in the same language and ecosystem as our firmware - Rust.
如此复杂的组件在三个平台上都很难开发、测试和维护。将这种客户端功能作为一个跨平台组件,可以在非移动平台上独立于用户界面进行测试,这似乎有很多优势,就像用我们的固件—— Rust 开发相同语言和生态系统的客户端一样。
Rust's pros and cons as a general purpose or systems programming language are discussed at great length in other places, but any language that has a bidirectional, transparent C ABI is highly amenable to integration into other language ecosystems. In the end, almost everything in modern software will be able to call into a Rust library.
Rust 作为一种通用或系统编程语言的优缺点在其他地方已经详细讨论过了,但是任何具有双向、透明的 C ABI 的语言都很容易集成到其他语言生态系统中。到最后,几乎所有现代软件都能调用 Rust 库。
An architecture for native cross platform development
原生跨平台开发架构
Cross compilation
交叉编译
Rust, as a system programming language has supported native targets for a bewildering number of chip sets and platforms. Usefully, this includes iOS and 64 bit android - 32 bit android can be a little more difficult due to some interesting design choices in its LibC design but it only a common target in embedded systems rather than handsets or tablets nowadays.
Rust,作为一种系统编程语言,支持大量的芯片集和平台的原生目标。有用的是,这包括 iOS 和 64 位 Android - 32 位 Android 可能有点困难,因为它的 LibC 设计中有一些有趣的设计选择,但它只是嵌入式系统的一个常见目标,而不是手机或平板电脑。
For Android, you will require a .so, which needs no additional tooling. For iOS, you will need a universal static library generated by the cargo-lipo tool.
对于 Android,您将需要 .so
,它不需要额外的工具。对于 iOS,你需要一个由cargo-lipo 工具生成的通用静态库。
C ABI / Swift interface
C ABI / Swift 接口
Rust has a built in way of declaring functions to be accessible via a C ABI, but it is convenient to use additional tooling to generate and maintain a header file automatically for use by swift and other potential consumers. The sn-bingen crate is at the time of writing the most well maintained tool for this.
Rust 采用了一种内置的方式来声明可以通过 C ABI 访问的函数,但是使用额外的工具来自动生成和维护头文件很方便,供 Swift 和其他潜在使用者使用。sn-bingen 包是在编写此工具时维护得最好的工具。
Android / JNI interface
Android / JNI 借口
As Android uses Java, you cannot directly call standard C ABI functions and must implement a JNI interface. The painful way to do this was via traditional tooling like SWIG, on top of the C interface. There is however now mature support for exporting a JNI interface directly from a Rust library using the reliably named JNI crate.
由于 Android 使用 Java,您不能直接调用标准的 C ABI 函数,必须实现一个 JNI 接口。要做到这一点,最痛苦的方法是在 C 接口之上通过传统工具(如 SWIG)。不过,现在已经有了成熟的支持,可以使用可靠的 JNI 包直接从 Rust 库导出 JNI 接口。
Rust interface
Rust 借口
The two binding interfaces, C and JNI can be conditionally compiled depending on target architecture and share dispatch to a Rust API shared between them. Both binding interfaces require unsafe code due to raw pointer use, but this can be contained within the interface functions. Once you reach this point in the abstraction, you have the full power of standard rust and its ecosystem.
这两个绑定接口 C 和 JNI 可以根据目标体系结构有条件地编译,并将分派共享到它们之间共享的 Rust API。由于原始指针的使用,两个绑定接口都需要不安全的代码,但这可以包含在接口函数中。一旦你达到了这个抽象点,你就拥有了标准 Rust 及其生态系统的全部力量。