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

3,706 阅读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 开发移动应用程序(如 AndroidiOS)的组织。他们的目标是通过创建和维护相关的 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工程包,由于我们想做一个 NativeAirbagSDK,所以我这边创建了一个子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函数,即便包名+类名都是正确的。切换成JavaOK

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…