Rust 学习笔记 - Android Rust 开发搭建流程

4,430 阅读4分钟

背景

最近持续在学习 Rust 相关知识,之前写了一篇 Rust 学习笔记 - Android Native 信号捕获(安全气囊)。在学习编写这部分代码的时候,我发现了一个痛点:每次修改 Rust 写的 Native 代码后,都需要将编译生成的.so 文件手动复制到 Android 项目中,这种重复劳动不仅繁琐,而且容易出错,而且Android Studio插件库检索不到Rust扩展,理论上它也是基于idea开发的🤔。

image.png

不过提高效率,寻思编写一个Python脚本,然后自动挪动,但发现这样做细节比较多,Build 生成的资源路径比较多,不规范且效率也不高。最近在 GitHub 上发现了一个欢迎度比较高的Gradle 插件,用的人比较多,使用起来非常方便,遇到的问题较少。特此记录并分享这个解决方案,希望对其他开发者有所帮助。

Rust开发必备仓库介绍

由 Mozilla (火狐浏览器最为知名)提供的插件,用于在 Android 项目中集成 Rust 代码。它简化了将 Rust 编译成 Android 原生库的过程,使开发者可以轻松地在 Android 应用中使用高性能的 Rust 代码,同时利用 Gradle 构建系统的便利性。

Rust Mobile 是一个支持 Rust 开发移动应用程序(如 Android 和 iOS)的组织。他们的目标是通过创建和维护相关的 Rust crates(库),促进移动开发方面的协作和工具开发。他们关注的问题包括标准化库的链接方式、SDK 版本控制以及 JNI 调用的处理方法。

配置依赖

首先,由于Android Studio不支持下载Rust插件,所以我仍然使用IntelliJ IDEA开发。 此时需要安装Android插件与Rust插件,并配置好Android SDK & NDK。

新建创建一个Android项目,我这边仍然以 NativeAirbag 为例。

Step 1

添加Rust Gradle插件。

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '8.2.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
    id 'org.jetbrains.kotlin.jvm' version '1.9.0' apply false
    id 'com.android.library' version '8.2.0' apply false
    id "org.mozilla.rust-android-gradle.rust-android" version "0.9.4"
}

Step 2

对 app 模块配置 Rust 插件。

plugins {
    id 'org.mozilla.rust-android-gradle.rust-android' // 坑 1 :一定在首位添加 不然会导致 elf 打包失败,找不到 elf 文件。
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

此时 async gradle 会报错。

image.png

缺少插件配置cargo的一些参数,我们先别急着配置。

Step 3

此时需要构建出我们的 Rust工程包,由于我们想做一个 NativeAirbag 的SDK,所以我这边创建了一个子module。

image.png

我们所有的逻辑通过 Rust 实现在这个 module,所以就在这个工程模块下创建。

image.png 我们可以使用命令

cd airbag/src/main/rust
cargo new airbag

这样就创建了一个Rust工程文件夹。

Step 4

第三步搞定之后我们就可以配置一下 插件中的 cargo 的参数了,同样在app模块下添加配置。

cargo {
    module = "../airbag/src/main/rust/airbag" // 配置一下 Rust 工程路径
    libname = "airbag" // 这里要与 Cargo.toml 中的 name 值一样
    targets = ["x86_64", "arm64"] // 指定 CPU架构,到时候编译时会生成对应的 elf 文件
//  profile = 'release'
    profile = 'debug'
}

这个 Cargo插件参数有很多配置,前三个字段是必须指定的。其他参数配置可以参考文档与源码酌情设置。

Step 5

由于我们改动 Rust 代码以后需要触发编译,然后在 Android 侧调用 JNI 等等去调试。

需要 每次去执行 ./gradlew cargoBuild 去构建,所以比较麻烦,创建一个 task 过滤器在执行构建 apk 打包的行为前,先触发Rust 编译,如下代码。

tasks.configureEach { task ->
    if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) {
//        task.dependsOn 'cargoBuild'
//        task.dependsOn 'cargoBuildArm'
        task.dependsOn 'cargoBuildArm64'
        task.dependsOn 'cargoBuildX86_64'
    }
}

Step 6

此时有时候我们触发./gradlew cargoBuild会报错,

/usr/bin/env bash /Users/edisonli/Downloads/NativeAirbag/build/linker-wrapper/linker-wrapper.sh
->/usr/bin/env bash /Users/edisonli/Downloads/NativeAirbag/build/linker-wrapper/linker-wrapper.sh
/Users/edisonli/Downloads/NativeAirbag/build/linker-wrapper/linker-wrapper.sh: line 4: : command not found

因为这个 Rust 插件需要结合 Python执行一些文件操作,因此需要指定 Python路径,如果你有这个问题,请在 local.properties 文件中添加 python路径。

rust.pythonCommand=/opt/homebrew/bin/python3

当然你也可以不用 python 可以配置其他的方式。

Step 7

执行 apk 打包命令的时候,Rust 编译好的 elf 文件会自动打包到 apk 中,非常的方便。 image.png

按照上述路径足以支持初学 Rust 语言并且希望可以在 Android 上实践的同学, 开始写 Rust 吧。

如果进一步入门可以参考如下几篇。

Rust 学习笔记 - JNI 调用篇

Rust 学习笔记 - Android Native 信号捕获(安全气囊)

TODO

现在遗留一些开发上的问题

如何断点调试?

待研究。

kotlin JNI调用找不到方法?

native方法无法找到Rust中的JNI函数,即便包名+类名都是正确的。切换成Java就OK。

2024-09-18 更新

测试的代码

image.png

修改后的代码

image.png

2024-11-13 更新

最近编译运行发现python版本升级到3.13版本以后,会导致编译时提示import pipes找不到,查询了一下再新版本中它已经被弃用了。

解决方案
rust.pythonCommand=/opt/homebrew/bin/python3.12

使用指定python 3.12版本

参考链接

github.com/mozilla/rus…