Rust 语言已经成为了越来越受欢迎的一种系统级编程语言。它被广泛使用来开发高性能的系统软件,模块化的库,以及并发和并行计算应用程序。不仅如此,它还可以为其他平台和设备生成二进制代码,包括 Android 操作系统。如果你也想在 Android 上利用 Rust 开发应用程序

创建Rust项目

首先创建 Rust 项目

cargo new rustDemo

为了跟前文(使用 GoMobile 创建 Android、iOS 跨平台 WebSocket Library)呼应,我们这里也使用 Rust 借助 tokio 写一个 WebSocket Server。

main.rs

use async_trait::async_trait;
use ezsockets::Error;
use ezsockets::Server;
use ezsockets::Socket;
use std::net::SocketAddr;

type SessionID = u16;
type Session = ezsockets::Session<SessionID, ()>;

struct EchoServer {}

#[async_trait]
impl ezsockets::ServerExt for EchoServer {
   type Session = EchoSession;
   type Call = ();

   async fn on_connect(
       &mut self,
       socket: Socket,
       address: SocketAddr,
       _args: (),
   ) -> Result<Session, Error> {
       let id = address.port();
       let session = Session::create(|handle| EchoSession { id, handle }, id, socket);
       Ok(session)
   }

   async fn on_disconnect(
       &mut self,
       _id: <Self::Session as ezsockets::SessionExt>::ID,
   ) -> Result<(), Error> {
       Ok(())
   }

   async fn on_call(&mut self, call: Self::Call) -> Result<(), Error> {
       let () = call;
       Ok(())
   }
}

struct EchoSession {
   handle: Session,
   id: SessionID,
}

#[async_trait]
impl ezsockets::SessionExt for EchoSession {
   type ID = SessionID;
   type Args = ();
   type Call = ();

   fn id(&self) -> &Self::ID {
       &self.id
   }

   async fn on_text(&mut self, text: String) -> Result<(), Error> {
       self.handle.text(text);
       Ok(())
   }

   async fn on_binary(&mut self, _bytes: Vec<u8>) -> Result<(), Error> {
       unimplemented!()
   }

   async fn on_call(&mut self, call: Self::Call) -> Result<(), Error> {
       let () = call;
       Ok(())
   }
}

#[tokio::main]
async fn main() {
   tracing_subscriber::fmt::init();
   let (server, _) = Server::create(|_server| EchoServer {});
   ezsockets::tungstenite::run(server, "127.0.0.1:9091", |_| async move { Ok(()) })
       .await
       .unwrap();
}

dependencies:

[dependencies]
async-trait = "0.1.67"
ezsockets = "0.5.1"
tokio = { version = "1.26.0", features = ["full"] }
tracing = "0.1.37"
tracing-subscriber = "0.3.16"

添加targets

> rustup target add aarch64-linux-android \
                  armv7-linux-androideabi \
                  i686-linux-android \
                  x86_64-linux-android

指定 linker

Rust 默认调用平台安装的 cc 编译器,所哟我们需要指定 Android 的 CC 和 AR

首先安装 Android NDK

部分教程说需要执行下面的命令:

python ${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 > --arch arm64 --install-dir NDK/arm64

其实是不用的。在新的 NDK 中已经自自带了。我们在项目跟目录创建.cargo/config

[target.aarch64-linux-android]
linker = '/home/ying/Android/Sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang'
ar = '/home/ying/Android/Sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar'

编译Rust代码

执行:

 cargo build --target aarch64-linux-android --release

会在 target/aarch64-linux-android/release 目录下生成产物

如果你使用 r23c 及以上版本,那么 cargo build 可能会出现以下错误,原因是 libgcc.a 已经被 libunwind.a 替代:

ld: error: unable to find library -lgcc

可以把把 libunwind.a 复制一份重命名为 libgcc.a,它的路径为:/ndk/25.2.9519653/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/14.0.7/lib/linux/aarch64/

在 Android 中运行

可以参考上篇文章使用 GoMobile 创建 Android、iOS 跨平台 WebSocket Library 稍微修改即可:

....

    MainScope().launch(Dispatchers.IO) {
        Runtime.getRuntime().exec("so 的路径")
    }
...

总结

要在 Android 设备运行 Rust 可执行我呢间需要:

  • 使用 rustup 添加 android 设备相关的 target
  • 项目中配置 linker ar
  • 编译 rust 代码
  • android 项目中执行 rust 文件