一个专注音视频领域问答的小圈子

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

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

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

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

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

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

  • 变量定义
  • 算术运算
  • for 循环语句,while 循环语句
  • 函数定义,函数调用
  • 递归
  • 静态类型系统
  • 类型推导
  • lambda 函数
  • 面向对象
  • 垃圾回收
  • 指针算术
  • goto 语句

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

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

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

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

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

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


rust 编译 so 实践

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

安装 rust

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

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

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

rustc --version

配置 rust 交叉编译

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

export ANDROID_HOME=/Users/$USER/Library/Android/sdk
export NDK_HOME=$ANDROID_HOME/ndk-bundle

然后执行如下命令:

mkdir NDK
${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm64 --install-dir NDK/arm64
${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm --install-dir NDK/arm
${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 文件,输入如下内容:

[target.aarch64-linux-android]
ar = "<path>/NDK/arm64/bin/aarch64-linux-android-ar"
linker = "<path>/NDK/arm64/bin/aarch64-linux-android-clang"

[target.armv7-linux-androideabi]
ar = "<path>/NDK/arm/bin/arm-linux-androideabi-ar"
linker = "<path>/NDK/arm/bin/arm-linux-androideabi-clang"

[target.i686-linux-android]
ar = "<path>/NDK/x86/bin/i686-linux-android-ar"
linker = "<path>/NDK/x86/bin/i686-linux-android-clang"

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

然后执行:

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

把 cargo-config.toml 移动到 ~/.cargo/ 目录下。

再执行如下命令:

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

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

rust 开发及编译

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

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

mkdir rust-android
cd rust-android

然后创建 rust 项目:

cargo new rust --lib
cd rust

rust 代表创建的项目所在文件夹名字。

--lib 代表我们创建的是一个库项目,而不是一个执行的二进制文件。

项目结构图如下:

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

Cargo.toml 就相当于 Android 中的 build.gradle ,一些第三方库都是通过这里去引用的。

lib.rs 就是具体编写代码的地方了。

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

[dependencies]
# 添加 jni 依赖,并指定版本
jni = { version = "0.10.2", default-features = false }

[profile.release]
lto = true

[lib]
# 编译的动态库名字
name = "rust"
# 编译类型 cdylib 指定为动态库
crate-type = ["cdylib"]

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

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

// 指定 os 类型
#![cfg(target_os = "android")]
// 允许不是 snake_case 的命令方式
#![allow(non_snake_case)]

// 引用标准库的一些内容
use std::ffi::{CString, CStr};
// 引用 jni 库的一些内容,就是上面添加的 jni 依赖
use jni::JNIEnv;
use jni::objects::{JObject, JString};
use jni::sys::jstring;

#[no_mangle]
// jni 函数调用的头
pub unsafe extern fn Java_com_glumes_rust_MainActivity_hello(
    env: JNIEnv, _: JObject, j_recipient: JString) -> jstring {
    // 将 Android 上层传来的字符串转出 rust 中的字符串变量
    let recipient = CString::from(
        CStr::from_ptr(
            env.get_string(j_recipient).unwrap().as_ptr()
        )
    );
    // 返回一个新的字符串
    let output = env.new_string("hello".to_owned() + recipient.to_str().unwrap()).unwrap();
    output.into_inner()
}

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

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

cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
cargo build --target i686-linux-android --release

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

然后把 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

知识星球

一个专注音视频领域问答的小圈子

公众号音视频开发进阶对应的知识星球,一个编程开发领域的专业圈子,贩卖知识和技巧!

※ 入群须知:了解该星球能提供的价值和帮助,在提问时务必阐述好背景,附带相关的信息。

iOS 用户可以加我微信 ezglumes 邀请你进星球,有疑问也可以加我微信咨询。

※ 星球内容:

基础教程:

在知识星球连载的干货教程,可以在专栏中找到,随着时间的推移,教程也会越来越多:

- 音视频基础概念
- WebRTC 入门教程及源码实践
- 播放器教程及源码实践
- OpenGL 和特效开发教程
- Vulkan 入门教程

部分内容可以在博客 https://glumes.com 中检索到,后面会在星球里持续更新.

干货分享:

涵盖了移动开发和音视频工程领域的绝大部分,从项目实战角度出发,提升能力,包括但不限于以下领域:

- Android/iOS 移动开发
- Camera 开发
- 短视频编辑 SDK 项目实践
- 在线直播和推流
- WebRTC 开发
- 播放器基础和提高
- OpenGL 图像渲染及特效开发
- C++ 基础和提高
- FFmpeg 使用和分析
- 干货资源和书籍分享

不止于技术方面的,各种 IT 新闻、茶余饭后、生活趣事也欢迎大家分享!!!

技术答疑解惑:

针对上述基础教程和干货分享的答疑,另外还有音视频和 IT 开发中的各种交流讨论。

- 基础知识点答疑
- 工业项目实践答疑
- 问题排查思路分析

一个 BUG 排查很久,不如来星球里提个问题,效率提升百倍。

求职和面试辅导:

一站式职场服务,每份工作都值得用心对待!!!

- 面试题和面试经验分享
- 简历修改和模拟面试
- 大厂内推和信息同步
- 职场经验分享
- 职业规划和发展分析

※ 星主和合伙人介绍

星主是公众号音视频开发进阶的作者,也是网站 https://glumes.com 的作者,曾参与过抖音、剪映等头部音视频 APP 底层 SDK 的开发。

合伙人也是在头条、快手从事音视频架构师的职位,具有多年的音视频开发经验,能力圈覆盖了音视频的绝大多数领域,资深音视频从业人员为你保驾护航。

微信公众号

扫描下面的二维码关注我的微信公众号《音视频开发进阶》,推送更多精彩内容!

添加我的微信 ezglumes 拉你入音视频与图形图像技术群一起交流学习~

wechat-account-qrcode

原创文章,转载请注明来源:    rust 开发编译 Android 动态库实践