前言
Rust是个好东西,Google也开始把它用于AOSP了。我们开发应用同样也可以使用Rust来编写原本为C++的Native代码。网上搜罗一圈,入门的文档不多linux系统安装不少,这里稍微归纳整理一下吧,毕竟Hello Worldhttp协议是人类的一大步。
安装Rust
Rust的文档真android下载安装的非常棒,目前的翻译版本也几乎满足所有学习需求。安装很简单,参考官网(Rust-lang.org)即可,一行命令:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
配置NDK
1、先确linux系统安装保你在Android Studio的SDK Manager中下载安装好了NDK相关的工具包,基操就不赘述了。
2、默认目录一般都在 /Users/你的用户名/Library/Android/sdk/ndk-bundle
这个位置,用户目录可以用 ${HOME}
代替。当然,如果你的SDK在其他位置,按你的来即可。
在任linux必学的60个命令意处创建一个名为NDK的目录linux常用命令(名字随意,也可不叫NDK),然后运行NDK工具包中的py脚本以编译NDK开发环境:
cd ~ mkdir NDK # 不同架构参数不同,按需配置即可,比如我就只需要arm64 # api参数最好按你的应用targetSDK的版本号来,比如我这里是30 # Python版本我这里是3.8,如果你是Python 2.x的话,不确定能否运行成功 python ${HOME}/Library/Android/sdk/ndk-bundle/build/tools/make_standalone_toolchain.py --api 30 --arch arm64 --install-dir NDK/arm64 python ${HOME}/Library/Android/sdk/ndk-bundle/build/tools/make_standalone_toolchain.py --api 30 --arch arm --install-dir NDK/arm python ${HOME}/Library/Android/sdk/ndk-bundle/build/tools/make_standalone_toolchain.py --api 30 --arch x86 --install-dir NDK/x86
3、编辑Rust环境的配置文件即 ~/.cargo/config
,若无,新建即可,Android并添加内容:
# 同样是按需配置,如果你不需要编译其他架构,就不添加 # 相关路径最好写绝对路径,此处若用${HOME}不生效 [target.aarch64-linux-android] ar = "/Users/你的用户名/NDK/arm64/bin/aarch64-linux-android-ar" linker = "/Users/你的用户名/NDK/arm64/bin/aarch64-linux-android-clang" [target.armv7-linux-androideabi] ar = "/Users/你的用户名/NDK/arm/bin/arm-linux-androideabi-ar" linker = "/Users/你的用户名/NDK/arm/bin/arm-linux-androideabi-clang" [target.i686-linux-android] ar = "/Users/你的用户名/NDK/x86/bin/i686-linux-android-ar" linker = "/Users/你的用户名/NDK/x86/bin/i686-linux-android-clang"
4、添加编译工具链,和第3android/yunos步中配置的对应:
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android
编写Demo
配置好各种环境后,就可以开始Coding了,先生成一个Rust的lib空项目,rusHTTPSt-android-libs
是我的自定义命名:
cargo new rust-android-libs --lib
进入目录,编辑Cargo.toml配置文件,直接修改如下:
[package] name = "rust-android-libs" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] jni = { version = "0.19.0", default-features = false } [lib] crate_type = ["cdylib"]
这里Rust的JNI版本,可以参考官方文档:Docs.rs,目前最新为0.19.0,下面的crate_type配置也是按文档来的。
3、编写Rust代码,在http代理src/lib.rs文件中:
#![cfg(target_os = "android")] #![allow(non_snake_case)] use jni::JNIEnv; use jni::objects::{JClass, JString}; use jni::sys::jstring; #[no_mangle] pub extern "C" fn Java_com_xxx_xxx_Yyy_getTestStr( env: JNIEnv, _: JClass, ) -> jstring { env.new_string("Hello World!") .expect("Couldn't create java string!") .into_inner() } #[no_mangle] pub extern "C" fn Java_com_xxx_xxx_Yyy_getTestStrWithInput( env: JNIEnv, _: JClass, input: JString, ) -> jstring { let input: String = env.get_string(input) .expect("Couldn't get java string!") .into(); let output = env.new_string(format!("Hello, {}!", input)) .expect("Couldn't create java string!"); output.into_inner() }
如上所示,我们编写了两个方法,一个是直接返回一个String,另一个是带参数返回拼接后的String。linux重启命令方法的命名规则和写C++的JNI代码一样,以此处为例,说明http 302我们需要在Java/Kotlin代码中对应创建一个包名为http 302 com.xxx.xxx
,名为 Yyy
的类,其中有两个方法:
package com.xxx.xxx object Yyy { init { // 因为等会儿编译的so产物为librust_android_libs.so,所以此处加载命名如下 System.loadLibrary("rust_android_libs") } external fun getTestStr(): String external fun getTestStrWithInput(input: String): String }
4、编译Ruhttpwatchst项目,按需要的架构编译即可,如果不用模拟器的话,一般都不用考虑x86:
cargo build --target aarch64-linux-android --release cargo build --target armv7-linux-androideabi --release cargo build --target i686-linux-android --release
编译成功后在项目的 /target/aarch64-linulinux系统安装x-android/release/librust_android_libs.so
路径下可以找到想要的so文件,把它复制到Android项目中。
一般来说都在 .../app/src/main/jniLibs/arm64-v8a/
这android下载样的目录架构图下,按你需要的架构来即可。同时记得配置build.gradle:
... android { compileSdkVersion 30 defaultConfig { ... ndk { abiFilters 'arm64-v8a' } } // 如果你的AGP插件版本不小于7.0,可能需要添加 packagingOptions { jniLibs { useLegacyPackaging = true } } } ...
后话
整个流程下来,还是有点小繁架构师工资琐的,同时也整理几个问题吧:
1、同样的代码逻辑,Rust编译出来的so库比C++编译的要大很多,以我上面的代码为https和http的区别例https域名,linux删除文件命令64位架构大约在4MB左右,32位在3MB多一点,而C++编译的产物分别为1MB多和900多KB,这对包体积大小敏感的项目来说,差距还是不小的。不知道有没有优化的方法,由于我也是刚接触Rust,还不太了解。
# 我们可以用strings命令来对比两种环境编译出来的so结构 strings librust_android_libs.so
2、复杂的业务逻辑肯定需要调试,以方便定位问题,http 302但以我们上面的例子来看,Rust项目和Android是分开的,如何像C++代码那样直接在Android项目中整合编译并断点调试,还需要进一步探索。
3、Google和Rust官android是什么手机牌子方对于适配到Android应用项目的相关文档还不是很丰富,我们在Studio中New Project的时候,就可以看见有Native C++的模板可以选择,这给了初学者很好的示范,希望以后也有Native Rust之类的模板。
评论(0)