最近关注了一波 rust,一门目前还比较小众但却很强大的编程语言,官网地址如下:

https://www.rust-lang.org/

rust 的学习曲线比较陡峭,在开始学习之前建议看看王垠的这篇文章 《如何掌握所有的编程语言》,地址如下:

https://www.yinwang.org/blog-cn/2017/07/06/master-pl

学习语言,重要的是掌握其语言特性。

王垠举了一些语言特性的例子:

看着这些特性是不是很像一些编程语言书的目录?

在学习 rust 的时候也可以照着这些语言特性去对比自己是否掌握了。

那么 rust 到底强大在哪里?在 Kotlin 刚出的时候宣传的点就是空安全 ,弥补 Java 在这方面的不足。而 rust 可以说对比的是 C++,弥补 C++ 在空指针和野指针(悬垂指针)方面的不足,当然 rust 的优势还不足如此。

以下是来自维基百科的介绍,有些特性我暂时还没体验过,先摘录一波:

Rust 是由 Mozilla 主导开发的通用、编译型编程语言。。设计准则为“安全、并发、实用”,支持函数式、并发式、过程式以及面向对象的编程风格。

目前国内也已经有一些团队在用 rust 进行开发了,可以在观望一波后,再决定是否投入精力入坑~~~


rust 编译 so 实践

下面是用 rust 编译 Android 动态库实践,主要参考了 Mozilla 给的例子,具体链接在后面。

安装 rust

首先是安装 rust ,通过如下命令安装:

1curl https://sh.rustup.rs -sSf | sh

然后通过如下命令输出 rust 版本以及验证是否安装成功。

1rustc --version

配置 rust 交叉编译

确保 Android 的 sdk 目录配置到了系统环境中。

1export ANDROID_HOME=/Users/$USER/Library/Android/sdk2export NDK_HOME=$ANDROID_HOME/ndk-bundleexport ANDROID_HOME=/Users/$USER/Library/Android/sdk
2export NDK_HOME=$ANDROID_HOME/ndk-bundle

然后执行如下命令:

1mkdir NDK2${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm64 --install-dir NDK/arm643${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm --install-dir NDK/arm4${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch x86 --install-dir NDK/x86
2${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm64 --install-dir NDK/arm64
3${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm --install-dir NDK/arm
4${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch x86 --install-dir NDK/x86

主要是执行 ndk-bundle 目录下的 python 脚本,该脚本在 NDK 目录中去创建 arm64、arm、x86 架构平台的编译环境。

在把环境添加到 rust 的配置中。

创建 cargo-config.toml

1[target.aarch64-linux-android] 2ar = "<path>/NDK/arm64/bin/aarch64-linux-android-ar" 3linker = "<path>/NDK/arm64/bin/aarch64-linux-android-clang" 4 5[target.armv7-linux-androideabi] 6ar = "<path>/NDK/arm/bin/arm-linux-androideabi-ar" 7linker = "<path>/NDK/arm/bin/arm-linux-androideabi-clang" 8 9[target.i686-linux-android]10ar = "<path>/NDK/x86/bin/i686-linux-android-ar"11linker = "<path>/NDK/x86/bin/i686-linux-android-clang"
 2ar = "<path>/NDK/arm64/bin/aarch64-linux-android-ar"
 3linker = "<path>/NDK/arm64/bin/aarch64-linux-android-clang"
 4
 5[target.armv7-linux-androideabi]
 6ar = "<path>/NDK/arm/bin/arm-linux-androideabi-ar"
 7linker = "<path>/NDK/arm/bin/arm-linux-androideabi-clang"
 8
 9[target.i686-linux-android]
10ar = "<path>/NDK/x86/bin/i686-linux-android-ar"
11linker = "<path>/NDK/x86/bin/i686-linux-android-clang"

记得把替换成 NDK 文件夹所在的路径。

然后执行:

1cp cargo-config.toml ~/.cargo/config

把 cargo-config.toml 移动到 ~/.cargo/

再执行如下命令:

1rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android

以上就完成了交叉编译环境的配置。

rust 开发及编译

现在要涉及到具体的 rust 开发了,推荐使用 JetBrains 系列的 IntelliJ IDEA ,无需激活,使用社区版就行,安装 rust 插件就可以愉快地编写代码了。

执行以下命令创建工程目录:

1mkdir rust-android2cd rust-android
2cd rust-android

然后创建 rust 项目:

1cargo new rust --lib2cd rust
2cd rust

rust--lib

项目结构图如下:

android studio rust 支持 rust开发安卓_linux

结构很简单,就两个文件,本篇文章也不会自己去新增文件,当然肯定是会有编译文件出现的。

Cargo.tomllib.rs

首先要在 Cargo.toml 中去添加一些内容:

1[dependencies] 2# 添加 jni 依赖,并指定版本 3jni = { version = "0.10.2", default-features = false } 4 5[profile.release] 6lto = true 7 8[lib] 9# 编译的动态库名字10name = "rust"11# 编译类型 cdylib 指定为动态库12crate-type = ["cdylib"]
 2# 添加 jni 依赖,并指定版本
 3jni = { version = "0.10.2", default-features = false }
 4
 5[profile.release]
 6lto = true
 7
 8[lib]
 9# 编译的动态库名字
10name = "rust"
11# 编译类型 cdylib 指定为动态库
12crate-type = ["cdylib"]

关于添加的内容含义如上注释,主要是添加了 jni 依赖和指定编译,有了 jni 依赖才能够让 Android 项目代码调用到 so 动态库的内容。

接下来就是编写 rust 代码了。

1// 指定 os 类型 2#![cfg(target_os = "android")] 3// 允许不是 snake_case 的命令方式 4#![allow(non_snake_case)] 5 6// 引用标准库的一些内容 7use std::ffi::{CString, CStr}; 8// 引用 jni 库的一些内容,就是上面添加的 jni 依赖 9use jni::JNIEnv;10use jni::objects::{JObject, JString};11use jni::sys::jstring;1213#[no_mangle]14// jni 函数调用的头15pub unsafe extern fn Java_com_glumes_rust_MainActivity_hello(16    env: JNIEnv, _: JObject, j_recipient: JString) -> jstring {17    // 将 Android 上层传来的字符串转出 rust 中的字符串变量18    let recipient = CString::from(19        CStr::from_ptr(20            env.get_string(j_recipient).unwrap().as_ptr()21        )22    );23    // 返回一个新的字符串24    let output = env.new_string("hello".to_owned() + recipient.to_str().unwrap()).unwrap();25    output.into_inner()26}
 2#![cfg(target_os = "android")]
 3// 允许不是 snake_case 的命令方式
 4#![allow(non_snake_case)]
 5
 6// 引用标准库的一些内容
 7use std::ffi::{CString, CStr};
 8// 引用 jni 库的一些内容,就是上面添加的 jni 依赖
 9use jni::JNIEnv;
10use jni::objects::{JObject, JString};
11use jni::sys::jstring;
12
13#[no_mangle]
14// jni 函数调用的头
15pub unsafe extern fn Java_com_glumes_rust_MainActivity_hello(
16    env: JNIEnv, _: JObject, j_recipient: JString) -> jstring {
17    // 将 Android 上层传来的字符串转出 rust 中的字符串变量
18    let recipient = CString::from(
19        CStr::from_ptr(
20            env.get_string(j_recipient).unwrap().as_ptr()
21        )
22    );
23    // 返回一个新的字符串
24    let output = env.new_string("hello".to_owned() + recipient.to_str().unwrap()).unwrap();
25    output.into_inner()
26}

抛开 rust 具体语法不看,代码内容也很简单,传一个字符串,返回一个新的字符串。

接下来就是执行具体的编译:

1cargo build --target aarch64-linux-android --release2cargo build --target armv7-linux-androideabi --release3cargo build --target i686-linux-android --release
2cargo build --target armv7-linux-androideabi --release
3cargo build --target i686-linux-android --release

分别执行上面三个命令,会分别构建三个不同平台的 so 库。

android studio rust 支持 rust开发安卓_Android_02

然后把 so 库替换到 Android 项目的 jniLibs 具体文件夹就好了。这样就完成了用 rust 编译 Android 平台的 so 动态库,并且每次编译后的时候就要进行 so 的替换,当然也可以想办法把 rust so 的编译放在 Android gradle 的编译过程中,就和用 CMake 编译一样。

问题和思考

以上只是一个小小的例子,想用 rust 实现像 C++ 那样去开发动态库,可能还一些坑要去探索。仅仅是实现 jni 的调用还是远不够的,在 NDK 开发里面还有很多头文件,如何去在 rust 里面去实现调用?

如果想要 rust 去打印 Android NDK 中的 log ,倒是可以参考 android_logger-rs 项目,至于其他的慢慢摸索中。

另外还有个问题,如何在 Android 中去断点调试 rust 的代码,在网上搜索了一番,还没找到合适的答案,有知道的朋友可以指点我一二 ?

参考

具体的例子可以参考我的 GitHub 项目:

https://github.com/glumes/rust-android-example

  1. https://www.yinwang.org/blog-cn/2017/07/06/master-pl
  2. https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html