rust 静态 android,Rust on Android

本文为 Medium 文章 Rust on Android 的翻译版本,供喜欢这门技术的开发者阅读使用,请不要用于任何商业用途。原文理解并不难,所以我建议你可以先尝试阅读一下英文原版。

你可能听说过 Rust,它是一门为内存安全和速度而设计的系统级编程语言。由 Mozilla 打造,旨在提供下一代高性能跨平台软件的能力。如果你还没有听过这门语言,我建议先看一下(入门教程)great learning meterial,但请记住,你可能要花一点时间才会对这门语言感兴趣和欣赏它,因此我建议可以多尝试一些而不只是写一个「Hello World」。 如果你是一名 Android 开发者你可能会问_怎样做_和_为什么_在 Android 开发中使用 Rust。这篇文章将尽可能解释_怎样做_。至于_为什么_,对于我们 Visly 来说最重要的原因是它能够在 Android 和 iOS 平台间以一种高性能和安全的方式共享代码,又比C++容易使用得多。

准备开始

在我们开始之前我们需要确保我们已经安装了 Rust 工具链。我们将假定你已经安装了 Android 工具链,如果没有你需要下载 Android Studio 并且根据其他的 Android 入门教程配置好 Android 环境。可以通过检查$ANDDROID_HOME变量来确保环境已经安装。如果你使用的是 macOS 的话,这个变量应该被设置为 ~/Library/Android/sdk。

下一步将在我们的操作系统上安装 Rust。Rustup让这一些只需要一行代码这么简单。curl https://sh.rustup.rs -sSf | sh

你可以通过 rustc --version 命令验证 Rust 是否正确安装和位于你的 PATH 路径下。一旦 Rust 在你的操作系统下安装成功我们需要确保 Rust 知道如何构建才能支持安卓架构。Rust 可以编译支持所有架构,但这不是默认行为。运行如下命令添加合适的架构。rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android

接下来我们需要配置一些独立的工具链让 Rust 支持 Android 支持的多种架构。这些只需要被安装一次并且每个项目不会配置多次,所以我们将把它们安装到 home 目录下而不是项目目录下。mkdir ~/.NDK

$(ANDROID_HOME)/ndk-bundle/build/tools/make_standalone_toolchain.py --api 26 --arch arm64 --install-dir ~/.NDK/arm64;

$(ANDROID_HOME)/ndk-bundle/build/tools/make_standalone_toolchain.py --api 26 --arch arm --install-dir ~/.NDK/arm;

$(ANDROID_HOME)/ndk-bundle/build/tools/make_standalone_toolchain.py --api 26 --arch x86 --install-dir ~/.NDK/x86;

最后我们将告诉 Rust 这些工具链的存在。在~/.cargo/config中添加如下内容,如果这个文件不存在就创建一个。[target.aarch64-linux-android]

ar = ".NDK/arm64/bin/aarch64-linux-android-ar"

linker = ".NDK/arm64/bin/aarch64-linux-android-clang"

[target.armv7-linux-androideabi]

ar = ".NDK/arm/bin/arm-linux-androideabi-ar"

linker = ".NDK/arm/bin/arm-linux-androideabi-clang"

[target.i686-linux-android]

ar = ".NDK/x86/bin/i686-linux-android-ar"

linker = ".NDK/x86/bin/i686-linux-android-clang"

Hello World

我们使用 Rust 编译一个小的 hello world 应用!我们先来创建一个 rust 库,然后继续创建我们的 Android Studio 工程。mkdir rust-android-example

cd rust-android-example

cargo new rust --lib

cd rust

如上代码将创建一个基本的由 cargo 管理的 rust 库,cargo 在 rust 的作用类似于 gradle,我们后续将在 Android Studio 工程中使用 gradle。--lib标记告诉 cargo 我们想要创建一个库(Library),而不是一个可执行二进制文件,在我们新创建的工程文件夹中我们可以找到Cargo.toml这个文件,它的作用类似build.gradle文件定义了你的库以及依赖的 metadata 信息。你还会找到src文件夹包含了 rust 源码。这个目录只包含lib.rs文件,文件中只包含一个简单的测试方法。删掉文件中的内容,替换成如下代码:#![cfg(target_os="android")]

#![allow(non_snake_case)]

use std::ffi::{CString, CStr};

use jni::JNIEnv;

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

use jni::sys::{jstring};

#[no_mangle]

pub unsafe extern fn Java_com_example_android_MainActivity_hello(env: JNIEnv, _: JObject, j_recipient: JString) -> jstring {

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()

}

代码最开始我们通过#[cfg(target_os="android"来告诉 rust 这个文件只在面向 Android 时使用,同时由于 JNI 需要驼峰法命名方法,这在 rust 中不是标准用法,因此需要通过#[allow(non_snake_case)]来允许。还有一些需要注意的是,由于需要和 Kotlin 交流,我们要使用 C 调用约定和 JNI,这意味着我们需要告诉 rust 不要 mangle 任何名字(#[no_mangle)。

下面我们定义了一个基本方法,通过给定字符串构造一个新的字符串。我们需要变换一个 jni 字符串为 C 字符串,在变换为一个 rust 字符串然后返回。Rust 的 jni 和 ffi 库让这个过程相当安全,后续我们将链接一些我们在 Visly 中使用的开发范式让这个操作更简单。由于 Kotlin 和 Rust 间的胶水代码可以非常的小,所以在一个更大的应用中,这不是一个问题。

我们同样需要更新Cargo.toml添加jni库的依赖,定义最终二进制的名称和如何编译它。[dependencies]

jni = { version = "0.10.2", default-features = false }

[profile.release]

lto = true

[lib]

name = "rust"

crate-type = ["cdylib"]

在我们开始 Android Studio 工程前的最后一件事情是编译二进制包。cargo build --target aarch64-linux-android --release

cargo build --target armv7-linux-androideabi --release

cargo build --target i686-linux-android --release

Android Studio

到了开始一个新的 Android Studio 工程,在模拟器中测试它的时候了。先去标准工程配置中,我们将使用 Kotlin ,当然你也可以使用 Java。我们将工程命名为 android 保存项目到 rust-android-example根目录中。e644bd80d162735b4500585dcbbff338.png

打开MainActivity.kt,粘贴如下代码。我们声明一个外部方法 hello 告诉 Android 寻找一个原生库方法叫做 Java_example_com_android_MainActivity_hello。在我们调用这个方法之前我们需要使用System.loadLibrary加载我们的库。package com.example.android

import android.support.v7.app.AppCompatActivity

import android.os.Bundle

import android.util.Log

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

System.loadLibrary("rust")

Log.d("rust", hello("World"))

}

external fun hello(to: String): String

}

如果这个时候尝试编译应用,会在启动时发生崩溃。因为我们没有包含原生库到工程中。使用如下代码拷贝进来。cd rust-android-example

mkdir android/app/src/main/jniLibs

mkdir android/app/src/main/jniLibs/arm64-v8a

mkdir android/app/src/main/jniLibs/armeabi-v7a

mkdir android/app/src/main/jniLibs/x86

cp rust/target/aarch64-linux-android/release/librust.so android/app/src/main/jniLibs/arm64-v8a/librust.so

cp rust/target/armv7-linux-androideabi/release/librust.so android/app/src/main/jniLibs/armeabi-v7a/librust.so

cp rust/target/i686-linux-android/release/librust.so android/app/src/main/jniLibs/x86/librust.so

现在我们可以再次编译和运行我们的应用,我们将看到「Hello World」在 Logcat 中输出。恭喜你!你已经成功的配置和运行 Rust 到 Android。

Automating the process

自动化拷贝二进制包这个过程可以由下面这个 bash 脚本轻松的办到。#!/bin/sh

JNI_LIBS=../android/app/src/main/jniLibs

cd rust

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

cargo build --target armv7-linux-androideabi --release

cargo build --target i686-linux-android --release

rm -rf $JNI_LIBS

mkdir $JNI_LIBS

mkdir $JNI_LIBS/arm64-v8a

mkdir $JNI_LIBS/armeabi-v7a

mkdir $JNI_LIBS/x86

cp target/aarch64-linux-android/release/librust.so $JNI_LIBS/arm64-v8a/librust.so

cp target/armv7-linux-androideabi/release/librust.so $JNI_LIBS/armeabi-v7a/librust.so

cp target/i686-linux-android/release/librust.so $JNI_LIBS/x86/librust.so\

保存上述代码到 rust-android-example/install.sh 并在任何一次更新了 rust 代码后运行它,编译和安装它到你的 Android Studio 工程中。如果你想 get fancy 你可以在 gradle 文件中将其添加为编译步骤,每次编译 Android Studio 工程前它都会被执行。

下一步

当上述代码工作时,并不是很容易掌握。在更大的工程中我们希望压缩那些用于 Kotlin 和 Rust 之间通信的丑陋二进制数字。在接下来的 post 中我将讲一下我们在构建 Visly 时用到的开发范式来简化这个工作。 在 Github 上可以找到本教程的实力代码,如果你计划在 Android 开发中使用 Rust,这将是一个很好的起点。如果你有任何问题可以给我发推。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值