跃迁引擎

空気を読んだ雨降らないでよ

iOS Research & Development


Rust - 移动端跨平台开发实践

1. 整体方案

img

Rust的移动端跨平台开发方案,具体如下

  1. 最底层SDK的业务逻辑由Rust实现
  2. 使用ffi工具,编写binding code
    1. Android、iOS:中间层使用uniffi编写binding代码。使用uniffi-bindgen将binding代码生成kotlin、Swift代码,方便Android、iOS调用。
    2. 鸿蒙:使用ohos-rs编写binding代码,使用ohos工具生成har包方便鸿蒙调用
  3. 构建工具
    1. Android: 最终使用ndk build的工具编译Android 平台的so
    2. iOS:未找到比较好用的工具,自行编写脚本构建xcframework
    3. 鸿蒙:最终使用ohrs build 构建鸿蒙平台的so

2. 代码详解

整个工程目录结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── doc
│ └── image
├── harmony
│ ├── dist
│ │ ├── strip
│ │ └── symbol
│ └── src
└── uniffi
├── dist
│ ├── android
│ └── ios
├── mobile_ffi
│ └── src
└── uniffi-bindgen
└── src

2.1 编译环境

  • Android 安装cargo-ndkcargo install cargo-ndk
  • iOS 需要安装 Xcode
  • 鸿蒙 需要安装 ohrs cargo install ohrs

该仓库可以作为一个Rust移动端跨平台开发的模版,通过init.py脚本可以快速初始化一个工程。

2.2 uniffi

img

该目录是workspace,包含mobile-ffi、uniffi-bindgen、dist, build_android_sh, build_ios_xc.sh

  • mobile-ffi

编写 rust 代码

  • uniffi-bindgen

该目录是源码依赖uniffi工具

  • build_android_sh
    • Android编译脚本
  • build_ios_xc.sh
    • iOS编译脚本
  • dist

该目录下包括Android、iOS的构建产物

  • Android: 包含生成的kotlin代码、带符号、无符号的so,其中so包含arm64-v8a、armeabi-v7a
  • iOS:包含生成的swift代码和xcframework,其中xcframework包含ios-arm64、ios-arm64_x86_64-simulator

2.3 harmony

该目录是依赖ohrs的能力,自动生成har包

  • 鸿蒙脚本

build_ohos.sh 是构建鸿蒙产物的脚本,运行 sh build_ohos.sh 即可

  • dist

该目录下包含鸿蒙平台的构建产物,包含带符号、无符号的so,其中so包含arm64-v8a、armeabi-v7a、x86_64

3. 使用姿势

3.1 iOS

假设需要能够在 arm64 架构下的 iOS 及 macOS 平台下使用,因此需要使用前面生成的库文件和绑定文件创建 xcframework 以便将不同平台和架构的库集成到单个 Framework 中。

3.1.1 环境配置

  • 安装xcode, 跳过
  • 添加工具链
1
rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios 
  • 安装 swiftformat
1
$ brew install swiftformat

3.1.2 iOS 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/bin/bash

# 主要步骤:
# 设置变量:
# FFI="mobile_ffi": 设置变量FFI,代表要处理的Rust库名称
# XC_NAME="mobile_rust": 设置变量XC_NAME,可能用于指定最终XCFramework的名称
# 进入项目目录:

# cd $FFI: 切换到由FFI变量指定的目录
# 为iOS目标构建Rust库:

# 使用for循环遍历iOS目标架构(aarch64-apple-iosaarch64-apple-ios-simx86_64-apple-ios)
# 如果目标是armv7s-apple-ios(尽管在循环中并未列出,可能是个遗留条件),则使用Rust的nightly版本和特定的Cargo特性进行构建
# 对于其他目标,使用默认的Cargo命令进行构建
# 注释掉的rustup target add $TARGET行表明原本可能计划自动安装缺少的目标,但后来决定不执行此操作
# 生成绑定:

# 使用cargo run --bin uniffi-bindgen generate命令生成Swift绑定,指定库文件和输出目录
# 重命名module.modulemap文件:

# 将生成的*.modulemap文件重命名为module.modulemap,以符合iOS项目的命名约定
# 移动Swift文件到项目目录:

# 删除目标项目目录中可能存在的旧Swift文件,然后将新生成的Swift文件移动到正确的位置
# 创建模拟器库:

# 使用lipo命令合并模拟器架构的库文件,创建一个适用于模拟器的库
# 注释掉的行表明原本可能还计划为其他架构(如armv7s-apple-ios)创建库,但后来决定不执行此操作
# 创建XCFramework:

# 删除旧的XCFramework(如果有),然后使用xcodebuild -create-xcframework命令创建一个新的XCFramework,包含模拟器和真机架构的库
# 清理:

# 删除临时目录和文件,以保持工作区的整洁

FFI="mobile_ffi"
cd $FFI
XC_NAME="mobile_rust"
# Add the iOS targets and build
for TARGET in \
aarch64-apple-ios \
aarch64-apple-ios-sim \
x86_64-apple-ios
do
# rustup target add $TARGET
if [ $TARGET == "armv7s-apple-ios" ]; then
cargo +nightly build -Z build-std=std --release --target=$TARGET --features=mock
else
cargo build --release --target=$TARGET
fi
done
cd -

# Generate bindings
cargo run --bin uniffi-bindgen generate --library ./target/aarch64-apple-ios/release/lib${FFI}.dylib --language swift --out-dir ./bindings

# Rename *.modulemap to module.modulemap
mv ./bindings/${FFI}FFI.modulemap ./bindings/module.modulemap
mkdir -p ./dist/ios && cp ./bindings/${FFI}.swift ./dist/ios

mkdir sim
lipo -create ./target/x86_64-apple-ios/release/lib${FFI}.a ./target/aarch64-apple-ios-sim/release/lib${FFI}.a -output ./sim/lib${FFI}.a
# lipo -create ./target/aarch64-apple-ios/release/lib${FFI}.a ./target/armv7s-apple-ios/release/lib${FFI}.a -output ./sm/lib${FFI}.a

# Recreate XCFramework
rm -rf "dist/ios/$XC_NAME.xcframework"
xcodebuild -create-xcframework \
-library ./sim/lib${FFI}.a -headers ./bindings \
-library ./target/aarch64-apple-ios/release/lib${FFI}.a -headers ./bindings \
-output "dist/ios/$XC_NAME.xcframework"

# Cleanup
rm -rf bindings sim ios

使用方法:

build_ios_xc.sh 是构建iOS产物的脚本,运行 sh build_ios_xc.sh 即可

3.1.3 生成xcframework

  • 首先我们执行上面的iOS脚本 sh build_ios_xc.sh,等执行完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
sh build_ios_xc.sh 
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
Compiling proc-macro2 v1.0.92
Compiling unicode-ident v1.0.14
Compiling serde v1.0.215
Compiling anyhow v1.0.93
Compiling camino v1.1.9
Compiling autocfg v1.4.0
Compiling thiserror v1.0.69
Compiling serde_json v1.0.133
Compiling semver v1.0.23
Compiling fs-err v2.11.0
Compiling paste v1.0.15
Compiling itoa v1.0.13
Compiling memchr v2.7.4
Compiling bytes v1.8.0
Compiling siphasher v0.3.11
Compiling quote v1.0.37
Compiling ryu v1.0.18
Compiling syn v2.0.89
Compiling log v0.4.22
Compiling once_cell v1.20.2
Compiling static_assertions v1.1.0
Compiling mobile v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile)
warning: unused variable: `e`
--> /Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile/src/log.rs:14:19
|
14 | .map_err(|e| anyhow!("set logger failed"))?;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
= note: `#[warn(unused_variables)]` on by default

warning: `mobile` (lib) generated 1 warning
Compiling uniffi_core v0.28.3
Compiling serde_derive v1.0.215
Compiling uniffi_checksum_derive v0.28.3
Compiling thiserror-impl v1.0.69
Compiling uniffi_meta v0.28.3
Compiling toml v0.5.11
Compiling bincode v1.3.3
Compiling cargo-platform v0.1.8
Compiling cargo_metadata v0.15.4
Compiling uniffi_macros v0.28.3
Compiling uniffi v0.28.3
Compiling mobile_ffi v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi)
Finished `release` profile [optimized + debuginfo] target(s) in 25.40s
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
Compiling serde v1.0.215
Compiling anyhow v1.0.93
Compiling semver v1.0.23
Compiling serde_json v1.0.133
Compiling camino v1.1.9
Compiling thiserror v1.0.69
Compiling ryu v1.0.18
Compiling itoa v1.0.13
Compiling memchr v2.7.4
Compiling once_cell v1.20.2
Compiling static_assertions v1.1.0
Compiling bytes v1.8.0
Compiling log v0.4.22
Compiling mobile v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile)
warning: unused variable: `e`
--> /Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile/src/log.rs:14:19
|
14 | .map_err(|e| anyhow!("set logger failed"))?;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
= note: `#[warn(unused_variables)]` on by default

warning: `mobile` (lib) generated 1 warning
Compiling uniffi_core v0.28.3
Compiling cargo-platform v0.1.8
Compiling cargo_metadata v0.15.4
Compiling uniffi v0.28.3
Compiling mobile_ffi v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi)
Finished `release` profile [optimized + debuginfo] target(s) in 13.09s
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
Compiling serde v1.0.215
Compiling anyhow v1.0.93
Compiling serde_json v1.0.133
Compiling thiserror v1.0.69
Compiling camino v1.1.9
Compiling semver v1.0.23
Compiling ryu v1.0.18
Compiling memchr v2.7.4
Compiling itoa v1.0.13
Compiling once_cell v1.20.2
Compiling bytes v1.8.0
Compiling static_assertions v1.1.0
Compiling log v0.4.22
Compiling mobile v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile)
warning: unused variable: `e`
--> /Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile/src/log.rs:14:19
|
14 | .map_err(|e| anyhow!("set logger failed"))?;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
= note: `#[warn(unused_variables)]` on by default

warning: `mobile` (lib) generated 1 warning
Compiling uniffi_core v0.28.3
Compiling cargo-platform v0.1.8
Compiling cargo_metadata v0.15.4
Compiling uniffi v0.28.3
Compiling mobile_ffi v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi)
Finished `release` profile [optimized + debuginfo] target(s) in 11.31s
/Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
Compiling memchr v2.7.4
Compiling minimal-lexical v0.2.1
Compiling anyhow v1.0.93
Compiling once_cell v1.20.2
Compiling ryu v1.0.18
Compiling itoa v1.0.13
Compiling serde v1.0.215
Compiling bytes v1.8.0
Compiling thiserror v1.0.69
Compiling fs-err v2.11.0
Compiling utf8parse v0.2.2
Compiling siphasher v0.3.11
Compiling anstyle-parse v0.2.6
Compiling heck v0.5.0
Compiling uniffi_meta v0.28.3
Compiling log v0.4.22
Compiling smawk v0.3.2
Compiling nom v7.1.3
Compiling anstyle-query v1.1.2
Compiling anstyle v1.0.10
Compiling is_terminal_polyfill v1.70.1
Compiling colorchoice v1.0.3
Compiling textwrap v0.16.1
Compiling scroll v0.12.0
Compiling clap_lex v0.7.3
Compiling anstream v0.6.18
Compiling askama_escape v0.10.3
Compiling plain v0.2.3
Compiling strsim v0.11.1
Compiling clap_derive v4.5.18
Compiling goblin v0.8.2
Compiling clap_builder v4.5.21
Compiling glob v0.3.1
Compiling static_assertions v1.1.0
Compiling uniffi_core v0.28.3
Compiling askama_parser v0.2.1
Compiling weedle2 v5.0.0
Compiling camino v1.1.9
Compiling semver v1.0.23
Compiling serde_json v1.0.133
Compiling cargo-platform v0.1.8
Compiling basic-toml v0.1.9
Compiling toml v0.5.11
Compiling bincode v1.3.3
Compiling cargo_metadata v0.15.4
Compiling clap v4.5.21
Compiling askama_derive v0.12.5
Compiling uniffi_testing v0.28.3
Compiling uniffi_macros v0.28.3
Compiling uniffi_udl v0.28.3
Compiling askama v0.12.1
Compiling uniffi_bindgen v0.28.3
Compiling uniffi v0.28.3
Compiling uniffi-bindgen v0.1.0 (/Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/uniffi-bindgen)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 20.30s
Running `target/debug/uniffi-bindgen generate --library ./target/aarch64-apple-ios/release/libmobile_ffi.dylib --language swift --out-dir ./bindings`
Warning: Unable to auto-format mobile_ffi.swift using swiftformat: Os { code: 2, kind: NotFound, message: "No such file or directory" }
rm: ./dist/ios/swift/mobile_ffi.swift: No such file or directory
xcframework successfully written out to: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/dist/ios/mobile.xcframework
  • 输出:
1
xcframework successfully written out to: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/dist/ios/mobile.xcframework  

img

3.1.4 将xcframework 以及 Swift 集成到xcode

  • 将xcframework 放到xcode

img

3.1.5 将Swift文件,拖动到工程中

img

3.1.6 调用rust生成的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@objcMembers
@objc(YCTestRustTool)
public class YCTestRustTool: NSObject {

public static func testRust() {

let content = sayHi()
print("来自rust的值" + content)

let map = UniffiHandleMap<Any>()
_ = map.insert(obj: "洋葱")
_ = map.insert(obj: "数学")
print("插入后的map=", map)
do {
let value = try map.get(handle: 1)
print("获取索引为1的值", value)
} catch {

}
do {
try map.remove(handle: 1)
print("移除后的map=", map)
} catch {

}
}
}

3.2 Android

3.2.1 环境配置

  • 安装ndk

    • 可以使用Android studio 安装,然后配置环境变量

    • img

    • img

    • 使用cargo 安装ndk

    • cargo install cargo-ndk
      
      1
      2
      3
      4
      5
      6
      7

      - 安装工具链

      - ```TypeScript
      rustup target add aarch64-linux-android
      rustup target add armv7-linux-androideabi
      rustup target add i686-linux-android x86_64-linux-android
  • 确保 ktlint 已安装

运行以下命令检查 ktlint 是否可用:

ktlint --version

如果未安装,可以使用以下命令安装:

1
2
curl -sSLO https://github.com/pinterest/ktlint/releases/latest/download/ktlint && chmod a+x ktlint
sudo mv ktlint /usr/local/bin/

3.2.2 Android 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<<'COMMENT'
主要用于构建和准备用于Android平台的Rust库,并生成对应的Kotlin绑定代码下面是详细的步骤解释:

1. 设置环境变量:
FFI="mobile_ffi":设置环境变量FFI的值为mobile_ffi,这个值将在后续步骤中用于指定库名和文件路径
ANDROID_STRIP="/Users/yangcong/Library/Android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip":设置环境变量ANDROID_STRIP,指向Android NDK中的llvm-strip工具路径llvm-strip用于移除二进制文件中的符号表,以减少文件大小

2. 构建Rust库:
cd $FFI:切换到mobile_ffi目录(即环境变量FFI指定的目录)
# cargo clean:这一行被注释掉了,如果取消注释,它将会清理之前的构建产物
cargo ndk build -r:使用cargo-ndk插件构建Rust库,-r参数可能表示“release”模式,但这不是cargo ndk的标准参数,可能是特定于环境的配置或脚本的一部分
cd ..:返回到上一级目录

3. 生成Kotlin绑定:
cargo run --bin uniffi-bindgen generate --library ./target/aarch64-linux-android/release/lib$FFI.so --language kotlin --out-dir ./bindings:使用uniffi-bindgen工具生成Kotlin绑定代码这里指定了生成的绑定是基于aarch64-linux-android架构的release版本的libmobile_ffi.so库,输出目录为./bindings

4. 准备Android发布文件:
cp -r bindings/com ./dist/android/kotlin:将生成的Kotlin绑定代码复制到发布目录的相应位置
# rm -rf bindings 移除bindings目录,以清理临时文件

5. 复制库文件到Android架构对应的目录:
cp ./target/aarch64-linux-android/release/lib$FFI.so ./dist/android/symbol/arm64-v8a/:将aarch64架构的库文件复制到arm64-v8a目录
cp ./target/armv7-linux-androideabi/release/lib$FFI.so ./dist/android/symbol/armeabi-v7a/:将armv7架构的库文件复制到armeabi-v7a目录

6. 使用llvm-strip移除符号表:
mv ./target/aarch64-linux-android/release/lib$FFI.so ./dist/android/strip/arm64-v8a/ 和 mv ./target/armv7-linux-androideabi/release/lib$FFI.so ./dist/android/strip/armeabi-v7a/:将库文件移动到用于剥离符号表的目录
${ANDROID_STRIP} --strip-all ./dist/android/strip/arm64-v8a/lib$FFI.so 和 ${ANDROID_STRIP} --strip-all ./dist/android/strip/armeabi-v7a/lib$FFI.so:使用llvm-strip工具移除指定库文件的所有符号表信息,以减少文件大小

这段脚本的目的是为了构建Rust库,生成对应的Kotlin绑定代码,并准备Android平台所需的库文件,包括复制库文件到正确的架构目录和使用llvm-strip工具移除符号表以减少文件大小

COMMENT

#!/bin/bash
FFI="mobile_ffi"

# 检查环境变量ANDROID_NDK_HOME是否存在
if [ -z "$ANDROID_NDK_HOME" ]; then
echo "ANDROID_NDK_HOME is not set."
else
echo "ANDROID_NDK_HOME is set to $ANDROID_NDK_PATH"
fi
ANDROID_STRIP=/Users/yangcong/Library/Android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip

cd $FFI
# cargo clean
cargo ndk build -r
cd ..
cargo run --bin uniffi-bindgen generate --library ./target/aarch64-linux-android/release/lib$FFI.so --language kotlin --out-dir ./bindings
mkdir -p ./dist/android/kotlin && cp -r bindings/com ./dist/android/kotlin
cp -r bindings/com ./dist/android/kotlin
cd bindings
rm -rf com
cd -
rm -rf bindings

mkdir -p ./dist/android/strip/arm64-v8a/ && mv ./target/aarch64-linux-android/release/lib$FFI.so ./dist/android/strip/arm64-v8a/
${ANDROID_STRIP} --strip-all ./dist/android/strip/arm64-v8a/lib$FFI.so
mkdir -p ./dist/android/strip/armeabi-v7a/ && mv ./target/armv7-linux-androideabi/release/lib$FFI.so ./dist/android/strip/armeabi-v7a/
${ANDROID_STRIP} --strip-all ./dist/android/strip/armeabi-v7a/lib$FFI.so

运行脚本:

build_android.sh 是构建Android产物的脚本,运行 sh build_android.sh 即可

3.2.3 生成.so 文件和kotlin文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
sh build_android.sh
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
Building armeabi-v7a (armv7-linux-androideabi)
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
warning: unused variable: `e`
--> /Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile/src/log.rs:14:19
|
14 | .map_err(|e| anyhow!("set logger failed"))?;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
= note: `#[warn(unused_variables)]` on by default

warning: `mobile` (lib) generated 1 warning
Finished `release` profile [optimized + debuginfo] target(s) in 0.12s
Building arm64-v8a (aarch64-linux-android)
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
warning: unused variable: `e`
--> /Users/yangcong/Documents/RustProject/mobile_rust_demo/mobile/src/log.rs:14:19
|
14 | .map_err(|e| anyhow!("set logger failed"))?;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
= note: `#[warn(unused_variables)]` on by default

warning: `mobile` (lib) generated 1 warning
Finished `release` profile [optimized + debuginfo] target(s) in 0.05s
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/Users/yangcong/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/mobile_ffi/Cargo.toml
workspace: /Users/yangcong/Documents/RustProject/mobile_rust_demo/uniffi/Cargo.toml
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/uniffi-bindgen generate --library ./target/aarch64-linux-android/release/libmobile_ffi.so --language kotlin --out-dir ./bindings`
Warning: Unable to auto-format mobile_ffi.kt using ktlint: Os { code: 2, kind: NotFound, message: "No such file or directory" }

生成对应的so 文件和kotlin文件

img

3.2.4 导入到工程

img

3.2.5 配置

  1. 配置 build.grade, 注意一定要是aar格式的。因为rust依赖的是 Java Native Access (JNA), 所以必须是aar的

img

  1. 修改setting.gradle

img

3.2.6 调用

img

3.3 Harmony

参考:https://ohos.rs/docs/usage/basic.html

3.3.1 答疑

  1. 我们可以直接在鸿蒙上使用原来双端或者其他端已经构建好的动态/静态链接库吗?

回答:不可以。

这就好比我们想要在iOS上面运行从安卓的NDK构建出来的动态链接库是一个道理,本身由于环境或者native使用问题或者C/C++基础库等一系列问题,导致无法跨系统直接使用。

3.3.2 环境配置

  1. 安装ohrs
1
cargo install ohrs
  1. 安装工具链
1
2
3
rustup target add aarch64-unknown-linux-ohos
rustup target add armv7-unknown-linux-ohos
rustup target add x86_64-unknown-linux-ohos
  1. 检查是否安装成功
1
2
3
4
5
6
➜  ~ ohrs doctor
Environment variable OHOS_NDK_HOME should be set.
Rust version should be >= 1.78.0.
Rustup target: aarch64-unknown-linux-ohos should be installed.
Rustup target: armv7-unknown-linux-ohos should be installed.
Rustup target: x86_64-unknown-linux-ohos should be installed.
  1. 配置环境变量

img

1
2
3
export OHOS_NDK_HOME=/Users/yangcong/Library/OpenHarmony/Sdk/12

export OHOS_NDK_HOME=/Users/swain/Library/OpenHarmony/Sdk/12

3.3.3 编写脚本

  • 思路一:按照Android的集成方式,生成.so 文件,.d.ts文件,自己去实现 cmake, cpp 流程,也能实现,但是工作量很大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FFI="mobile_oh"
OHOS_NDK=/Users/yangcong/Library/OpenHarmony/Sdk/12
OHOS_NDK_HOME=${OHOS_NDK} ohrs build --dist=./dist/symbol/ --release

cd dist
rm -rf strip
mkdir strip
cp -r symbol/* ./strip/
cd -

# strip so debuginfo
${OHOS_NDK}/native/llvm/bin/llvm-strip --strip-all ./dist/strip/arm64-v8a/lib${FFI}.so
${OHOS_NDK}/native/llvm/bin/llvm-strip --strip-all ./dist/strip/armeabi-v7a/lib${FFI}.so
${OHOS_NDK}/native/llvm/bin/llvm-strip --strip-all ./dist/strip/x86_64/lib${FFI}.so

img

  • 思路二:网上提供了一个新的思路

    • 使用ohrs 创建工程

    •  ohrs init mobile -p=@ohrs/mobile
      
      1
      2
      3
      4
      5
      6
      7
      8

      - 使用ohrs build 和 ohrs artifact 直接生成har包,修改后的脚本如下

      - ```TypeScript
      OHOS_NDK=/Users/yangcong/Library/OpenHarmony/Sdk/12
      OHOS_NDK_HOME=${OHOS_NDK} ohrs build --release

      ohrs artifact

执行脚本,就会生成下面的目录结构

img

生成的内容很多,但是对我们有用的就是最后的 har 包

3.3.4 工程接入

  1. 导入到工程

img

  1. 调用

img

最近的文章

WKWebView缓存协议验证

前言本次我们基于 URLRequest.CachePolicy 协议进行 WKWebView 缓存能力的验证,主要验证方向为网页页面及相应资源的内容缓存。 API 如下 1234567891011public struct URLRequest : ReferenceConvertible, E …

, , 开始阅读
更早的文章

HarmonyOS - 使用画布绘制自定义图形 (Canvas)

Canvas提供画布组件,用于自定义绘制图形,开发者使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制,绘制对象可以是基础形状、文本、图片等。 使用画布组件绘制自定义图形可以由以下三种形式在 …

, , , 开始阅读
comments powered by Disqus