Android中使用Rust構(gòu)建so庫(kù)(一)

image.png

0x01 Introduction

Rust語(yǔ)言突然很火,其運(yùn)行速度堪比C++畏妖,有傳言說可能會(huì)成為C/C++的替代品梗顺。但是這不是今天討論的話題畴栖。還是來看下Rust強(qiáng)大的交叉編譯吧,用Rust構(gòu)建so庫(kù)。網(wǎng)絡(luò)上大都是在Linux系統(tǒng)下操作,包括官方文檔也都是以Linux系統(tǒng)為例說明的。今天我們就來詳細(xì)的講一下如何在Windows下使用Rust構(gòu)建so庫(kù)惑灵,以及Rust構(gòu)建的so文件與傳統(tǒng)的C/C++有什么區(qū)別呢?

0x02 Tools

主要工具:

  1. Android Studio——編寫Android Demo的工具
  2. IDEA(VS Code也可以)——編寫Rust語(yǔ)言的工具
  3. Python——執(zhí)行生成工具鏈的 python腳本

其它工具(非必須):

  1. IDA——反編譯so文件

0x03 Environment

  1. 安裝Rust語(yǔ)言環(huán)境
  2. 安裝Python環(huán)境
  3. 安裝Android SDK和Android NDK環(huán)境

0x04 Code

新建項(xiàng)目

IDEA安裝Rust插件眼耀,新建項(xiàng)目英支。新建項(xiàng)目的項(xiàng)目名稱如果存在多個(gè)單詞建議使用下劃線分隔,比如rust_jni_android哮伟,防止后面編譯報(bào)warning干花。

添加依賴

Cargo.toml中添加JNI依賴,并聲明lib.rscrate_typecdylib楞黄。告知編譯器要編譯成庫(kù)池凄。這樣將會(huì)構(gòu)建出動(dòng)態(tài)庫(kù) (.so, .dylib 或 .dll 文件,取決你的操作系統(tǒng)類型)鬼廓。

[dependencies]
# 添加 jni 依賴肿仑,并指定版本  0.17.0 截至目前是最新的版本
jni = { version = "0.17.0", default-features = false }

[lib] 
crate_type = ["cdylib"]
編寫代碼

coding...

#![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_logproject_NativeMethodTest_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()
}

#[no_mangle]
pub unsafe extern fn Java_com_example_logproject_NativeMethodTest_init(env: JNIEnv, jclass: JObject)
                                                                       -> jstring {

    // 將某個(gè)setEnable方法設(shè)置為true
    let clazz = env.find_class("com/example/logproject/SDK").unwrap();
    env.call_static_method(clazz, "setEnable", "(Z)V", &[JValue::from(true)]);

    let str = "i'm a so by rust!";
    let str = env.new_string(str.to_owned()).unwrap();
    str.into_inner()
}

0x04 Building

準(zhǔn)備工具鏈

用Python編譯工具鏈。如果Android NDK安裝的是默認(rèn)路徑碎税,在任意位置新建一個(gè)文件夾(建議不要帶有中文路徑)尤慰,然后使用CMD或者PowerShell執(zhí)行下面的代碼。如果NDK的位置不是默認(rèn)安裝位置雷蹂,務(wù)必更換NDK_HOME的地址伟端。

注:--api 29中的29是Android SDK中下載的API版本。我這里項(xiàng)目中用的androidx萎河,也下載過API 28的SDK荔泳,依然可以正常運(yùn)行。

$NDK_HOME="$env:LOCALAPPDATA\Android\Sdk\ndk-bundle"
mkdir NDK
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch arm64 --install-dir NDK/arm64
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch arm --install-dir NDK/arm
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch x86 --install-dir NDK/x86

生成過程大約需要2-3分鐘虐杯,下面是生成結(jié)果玛歌。


生成NDK工具鏈
創(chuàng)建Cargo配置文件

進(jìn)入到Cargo的安裝目錄,查看目錄下是否有config文件擎椰,沒有就創(chuàng)建支子。默認(rèn)應(yīng)該是沒有config文件的。
注:config文件不需要擴(kuò)展名达舒。

Cargo安裝目錄

在config文件中添加以下內(nèi)容值朋,其中ar和linker中的路徑請(qǐng)修改為上面創(chuàng)建工具鏈的路徑

[target.aarch64-linux-android]
ar = "E:\\rust_jni\\NDK\\arm64\\bin\\aarch64-linux-android-ar.exe"
linker = "E:\\rust_jni\\NDK\\arm64\\bin\\aarch64-linux-android-clang.cmd"

[target.armv7-linux-androideabi]
ar = "E:\\rust_jni\\NDK\\arm\\bin\\arm-linux-androideabi-ar.exe"
linker = "E:\\rust_jni\\NDK\\arm\\bin\\arm-linux-androideabi-clang.cmd"

[target.i686-linux-android]
ar = "E:\\rust_jni\\NDK\\x86\\bin\\i686-linux-android-ar.exe"
linker = "E:\\rust_jni\\NDK\\x86\\bin\\i686-linux-android-clang.cmd"
Cargo config文件
安裝交叉編譯組件

在CMD或者PowerShell執(zhí)行下面的代碼,來支持各平臺(tái)的編譯巩搏,依然需要等待幾分鐘昨登。
其中,各平臺(tái)與Android NDK filter的映射關(guān)系如下:
aarch64-linux-android 對(duì)應(yīng) arm64-v8a
armv7-linux-androideabi 對(duì)應(yīng) armeabi-v7a
i686-linux-android 對(duì)應(yīng) x86

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android
生成so庫(kù)

三個(gè)平臺(tái)需要以下三條命令分別生成贯底,這里我僅測(cè)試過生成arm64-v8aarmeabi-v7a的so庫(kù)丰辣。

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

armeabi-v7a為例,打開在IDEA中剛才編寫的代碼禽捆,在控制臺(tái)中輸入編譯命令笙什,回車。

編譯so

so庫(kù)在哪里呢胚想?展開target目錄琐凭,我這里編譯了兩次,所以存在兩個(gè)編譯文件

target目錄

然后在展開armv7-linux-androideabi-release-deps-librust_jni_android,

so文件位置

然后拷到android的項(xiàng)目目錄就行了浊服。


android項(xiàng)目目錄

0x05 Finally

所有的工作都已經(jīng)完成了统屈,生成的so也可以使用。
但是我發(fā)現(xiàn)幾個(gè)問題:

  1. 生成的so文件在Android 5.1的系統(tǒng)可能會(huì)崩潰牙躺,Android 10正常鸿吆。也有可能是我5.1手機(jī)的問題。我需要再進(jìn)一步驗(yàn)證述呐。
  2. 調(diào)試so文件比較復(fù)雜惩淳,我還沒找到好的調(diào)試方法,如果有好的方法乓搬,大家可以分享下思犁。

關(guān)于源碼,我會(huì)在寫完第二節(jié)放出进肯。

參考資料:
rust-jni:https://docs.rs/jni/0.17.0/jni/
Building and Deploying aRust library on Android:https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末激蹲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子江掩,更是在濱河造成了極大的恐慌学辱,老刑警劉巖乘瓤,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異策泣,居然都是意外死亡衙傀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門萨咕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來统抬,“玉大人,你說我怎么就攤上這事危队〈辖ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵茫陆,是天一觀的道長(zhǎng)金麸。 經(jīng)常有香客問我,道長(zhǎng)簿盅,這世上最難降的妖魔是什么钱骂? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮挪鹏,結(jié)果婚禮上见秽,老公的妹妹穿的比我還像新娘。我一直安慰自己讨盒,他們只是感情好解取,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著返顺,像睡著了一般禀苦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遂鹊,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天振乏,我揣著相機(jī)與錄音,去河邊找鬼秉扑。 笑死慧邮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舟陆。 我是一名探鬼主播误澳,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秦躯!你這毒婦竟也來了忆谓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤踱承,失蹤者是張志新(化名)和其女友劉穎倡缠,沒想到半個(gè)月后哨免,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昙沦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年琢唾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桅滋。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慧耍,死狀恐怖身辨,靈堂內(nèi)的尸體忽然破棺而出丐谋,到底是詐尸還是另有隱情,我是刑警寧澤煌珊,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布号俐,位于F島的核電站,受9級(jí)特大地震影響定庵,放射性物質(zhì)發(fā)生泄漏吏饿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一蔬浙、第九天 我趴在偏房一處隱蔽的房頂上張望猪落。 院中可真熱鬧,春花似錦畴博、人聲如沸笨忌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)官疲。三九已至,卻和暖如春亮隙,著一層夾襖步出監(jiān)牢的瞬間途凫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工溢吻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留维费,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓促王,卻偏偏與公主長(zhǎng)得像掩完,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子硼砰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355