Rust库交叉编译以及在Android中使用

安装Rust

Rust的安装很简单,参考官网即可,一行命令:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

配置NDK

  • 先确保你在Android Studio的SDK Manager中下载安装好了NDK相关的工具包,基操就不赘述了。

  • 默认目录一般都在 /Users/你的用户名/Library/Android/sdk/ndk 这个位置,用户目录可以用 ${HOME} 代替。当然,如果你的SDK在其他位置,按你的来即可。
    可以看到我电脑上安装了3个版本的ndk包。

    image

开始开发

  • 创建rust项目
    首先使用cargo创建一个rust项目
cargo new rust-libs-demo --lib
  • 修改配置文件
    进入目录 rust-libs-demo,编辑Cargo.toml配置文件,直接修改如下:
[package]
name = "rust-libs-demo"
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.21.1", default-features = false }

[lib]
crate-type = ["cdylib"]

这里Rust的JNI版本,可以参考官方文档:Docs.rs,crate_type配置也是按文档来的。

jni这个库则是 Rust 中用于与 Java Native Interface (JNI) 交互的crate。Java Native Interface (JNI) 允许Java代码与本地(即非Java虚拟机)应用程序库进行交互,这使得在Java中调用本地代码成为可能。而 jni crate 为Rust提供了与JNI进行交互的能力,使得Rust可以作为本地代码被Java调用。
通过 jni crate,你可以编写Rust代码来实现Java接口,并将其编译为本地库,然后在Java中使用JNI调用这些本地库。这使得你可以利用Rust的性能和安全性优势来实现对性能要求较高的部分,并且可以与现有的Java代码相集成。

  • 添加编译工具链(全局)
    接下来我们需要配置项目的编译架构,在.cargo/config中添加下面这些内容:
[target.aarch64-linux-android]
ar = "/Users/${你的用户名}/Library/Android/sdk/ndk/${ndk版本号}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"
linker = "/Users/${你的用户名}/Library/Android/sdk/ndk/${ndk版本号}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang"

[target.armv7-linux-androideabi]
ar = "/Users/kurisu/${你的用户名}/Android/sdk/ndk/${ndk版本号}/toolchains/llvm/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar"
linker = "/Users/${你的用户名}/Library/Android/sdk/ndk/${ndk版本号}/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi24-clang"

其中需要替换掉你的用户名和ndk版本号,根据你的实际情况来修改。整个地址需要是有效的,也就是要找到对应文件才行,上面的案例只加了 armv7a、aarch64架构的ndk,如果需要其他的要自行添加比如x86架构。

然后我们需要全局添加编译工具链,与上面配置的架构对应。比如上面只有两个架构就需要添加他们的编译工具链:

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

可以使用rustup target list查看全部的编译工具链。

  • 编写rust代码

首先删除项目内的main.rs文件,然后新建一个lib.rs文件,编写demo代码:

use jni::JNIEnv;

use jni::objects::{JClass, JString};

use jni::sys::jstring;

#[no_mangle]
pub extern "system" fn Java_com_mona_HelloWorld_hello<'local>(mut env: JNIEnv<'local>,
                                                     _: JClass<'local>,
                                                     input: JString<'local>)
                                                     -> 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_raw()
}
  • 编译Rust项目打包成.so包
    执行cargo build --target aarch64-linux-android --target armv7-linux-androideabi --release
    编译成功后在项目的 /target/aarch64-linux-android/release/librust_libs_demo.so/target/armv7-linux-androideabi/release/librust_libs_demo.so 路径下可以找到想要的so文件,把它复制到Android项目中。
    一般放在app/src/main/jniLibs/中根据架构来,如果你的AGP插件版本不小于7.0,可能需要修改build.gradle:
android {
  ....
    packagingOptions {
        jniLibs {
            useLegacyPackaging = true
        }
    }
    ....
}

demo代码地址

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×