在原生swift/java/fluuter里调用rust的库
rust项目demo
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
use std::ffi::{c_char, CString, CStr};
use std::thread;
use super::services;
/// 1. 无返回值接口
#[no_mangle]
pub extern "C" fn do_something() {
println!("api.rs: do_something");
services::do_internal_work();
}
/// 2. 同步返回值接口
#[no_mangle]
pub extern "C" fn get_sync_result() -> *mut c_char {
println!("api.rs: get_sync_result");
let result = services::get_result_string();
result.into_raw()
}
/// 回调函数类型(用于异步调用)
type CallbackFn = extern "C" fn(result: *const c_char);
/// 3. 异步执行任务 + 回调
#[no_mangle]
pub extern "C" fn run_async_task(callback: CallbackFn) {
println!("api.rs: run_async_task");
thread::spawn(move || {
println!("api.rs: Running async task...");
let result = services::simulate_async_result();
callback(result.as_ptr());
std::mem::forget(result); // 避免释放
});
}
/// 3.1 带上下文参数的异步任务 + 回调
#[no_mangle]
pub unsafe extern "C" fn run_async_task_with_name(name: *const c_char, callback: CallbackFn) {
println!("api.rs: run_async_task_with_name");
assert!(!name.is_null());
let name_cstr = CStr::from_ptr(name);
let name_str = name_cstr.to_string_lossy().to_string();
std::thread::spawn(move || {
println!("api.rs: Running async task for '{}'", name_str);
let result = services::simulate_async_result();
let full_result = CString::new(format!("{} -> {}", name_str, result.to_str().unwrap())).unwrap();
callback(full_result.as_ptr());
std::mem::forget(full_result);
});
}
/// 4. Rust 主动触发回调(模拟 push 机制)
static mut GLOBAL_CALLBACK: Option<CallbackFn> = None;
/// 注册回调
#[no_mangle]
pub extern "C" fn register_callback(callback: CallbackFn) {
unsafe {
GLOBAL_CALLBACK = Some(callback);
}
}
/// 主动触发回调
#[no_mangle]
pub extern "C" fn trigger_callback_from_rust() {
let msg = CString::new("Triggered from Rust (push event)").unwrap();
unsafe {
if let Some(cb) = GLOBAL_CALLBACK {
cb(msg.as_ptr());
}
std::mem::forget(msg);
}
}
配置文件Cargo.toml
1
2
3
4
5
6
7
8
9
10
11
12
[package]
name = "my_rust_lib"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full"] }
[lib]
name = "my_rust_lib"
crate-type = ["staticlib", "cdylib"]
配置自动生成.h文件 cbindgen.toml
1
2
3
4
5
6
7
language = "C"
include_version = true
pragma_once = true
[export]
include = ["do_something", "get_sync_result", "run_async_task", "run_async_task_with_name", "register_callback", "trigger_callback_from_rust"]
单独生成头文件 生成的方法必须要有关键字 #[no_mangle]
pub extern "C" fun
1
2
3
4
5
6
# --crate 你的 crate 名(可以是当前目录的 Cargo.toml 中 [package] name)
# --output 指定 .h 文件输出路径
# --config 可选:指定配置文件(推荐)
cbindgen --config cbindgen.toml --crate xxx --output include/my_rust_lib_bridge.h
自动生成头文件及xcframework的脚本build_all.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
#!/bin/bash
set -e
CRATE_NAME="my_rust_lib"
IOS_OUTPUT_DIR="output/ios"
HEADER_OUTPUT="output/include/${CRATE_NAME}.h"
# 清理旧产物
rm -rf output
mkdir -p "$IOS_OUTPUT_DIR" "$(dirname "$HEADER_OUTPUT")"
echo "🔨 Step 1: 编译 iOS 真机 arm64"
cargo build --release --target aarch64-apple-ios
echo "🔨 Step 2: 编译 iOS 模拟器 arm64"
cargo build --release --target aarch64-apple-ios-sim
echo "📄 Step 3: 生成 C 头文件 (.h)"
cbindgen --crate $CRATE_NAME --output $HEADER_OUTPUT
echo "🧩 Step 4: 创建 .xcframework (真机和模拟器都是 arm64)"
xcodebuild -create-xcframework \
-library target/aarch64-apple-ios/release/lib${CRATE_NAME}.a \
-headers output/include \
-library target/aarch64-apple-ios-sim/release/lib${CRATE_NAME}.a \
-headers output/include \
-output ${IOS_OUTPUT_DIR}/${CRATE_NAME}.xcframework
echo "✅ 完成,生成路径:${IOS_OUTPUT_DIR}/${CRATE_NAME}.xcframework"
tree output
# 前提,需要安装和配置
# brew install cbindgen lipo
# rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
# rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
一、iOS里swift项目接入测试 1、创建一个swift项目 2、将生成的xcframework拖到项目里 3、TARGETS -> Frameworks, Libraries, and Embeded Content -> 确保有, 且Embed & sign 4、配置桥接文件xxx.h
1
#include "my_rust_lib.h" // 这里是你生成的 .h 文件名,需拷贝到项目里或指定路径
5、配置 Objective-C Bridging Header里配置确定的桥接文件
6、在Appdelegate.swift里调用
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
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 同步调用无返回值
do_something()
// 同步调用,同步返回值
print("swift: get_sync_result")
if let cStringPtr = get_sync_result() {
let str = String(cString: cStringPtr)
print("swift: \(str)")
}
// 调用,异步返回
run_async_task_with_name("aaa".allocateUnsafeCString(), { cStringPtr in
if let cStringPtr = cStringPtr {
let str = String(cString: cStringPtr)
print("swift-run_async_task_with_name: \(str)")
}
})
// 注册调用
register_callback({ cStringPtr in
if let cStringPtr = cStringPtr {
let str = String(cString: cStringPtr)
print("swift-register_callback: \(str)")
}
})
trigger_callback_from_rust()
return true
}
在flutter里手动调用接入rust的库
方式1、 1、打开Runner.xcworkspace
项目,将打包出来的xxx.xcframework
加入到项目里
2、配置、TARGETS -> Frameworks, Libraries, and Embeded Content -> 确保有, 且Embed & sign
3、在Runner-Bridging-Header.h
里加入库里的头文件#include "my_rust_lib.h"
4、在Appdelegate.swift里调用
1
2
3
4
5
6
7
8
9
10
11
12
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 调用下库里的方法,这样在flutter调用时才能找到库
// 不然会报错:
// Invalid arguments(s): Failed to lookup symbol 'get_sync_result': dlsym(RTLD_DEFAULT, get_sync_result):sysmbol not found
do_something()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
5、在flutter里调用
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
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
typedef GetSyncResultNative = Pointer<Utf8> Function();
typedef GetSyncResultDart = Pointer<Utf8> Function();
final dylib = DynamicLibrary.process();
final GetSyncResultDart getSyncResult = dylib
.lookupFunction<GetSyncResultNative, GetSyncResultDart>('get_sync_result');
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String rustResult = "Waiting for Rust...";
@override
void initState() {
super.initState();
_callRust();
}
void _callRust() {
final ptr = getSyncResult();
final result = ptr.toDartString();
// 不调用 freeRustString,避免找不到符号
setState(() {
rustResult = result;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Rust FFI Demo')),
body: Center(
child: Text(rustResult, style: const TextStyle(fontSize: 24)),
),
),
);
}
}
问题:
Invalid arguments(s): Failed to lookup symbol 'get_sync_result': dlsym(RTLD_DEFAULT, get_sync_result):sysmbol not found
方案一、在原生代码里调用一次有使用记录
必须要Runner
项目原生代码调用一次,否则被自动优化了
方案二: Other Linker Flags
里设置-force_load
保证符号不会被优化掉 Xcode 和链接器有时会剔除未使用符号,导致运行时找不到。
常用技巧:
在 AppDelegate 的某个地方调用一次 Rust 函数(哪怕是空调用),保证链接器保留符号
或者用 -force_load 链接参数强制加载静态库全部符号
1
2
3
4
5
# 在 Xcode 的 Build Settings -> Other Linker Flags 添加
# 针对不同架构,你可能需要添加多个 -force_load,比如:
-force_load
${SRCROOT}/Frameworks/my_rust_lib.xcframework/ios-arm64-simulator/libmy_rust_lib.a
手动使用flutter_rust_bridge_codegen
1、在rust项目的Cargo.toml
里新增配置flutter_rust_bridge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[package]
name = "my_rust_lib"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full"] }
flutter_rust_bridge = "=2.10.0"
[lib]
name = "my_rust_lib"
crate-type = ["staticlib", "cdylib"]
使用脚步生成对应的dart文件
1
2
3
4
5
flutter_rust_bridge_codegen generate \
--rust-input crate::api \
--rust-root ../../my_rust_lib \
--dart-output lib/rust \
--c-output ../../my_rust_lib/src/bridge_generated.rs
生成Framework文件
项目里使用main.dart
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
import 'package:flutter/material.dart';
import 'rust/frb_generated.dart';
import 'rust/api/app_api.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化 RustLib
await RustLib.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _result = '';
// 调用同步返回值接口
Future<void> _callGetSyncResult() async {
final result = await getSyncResult();
setState(() {
_result = '同步结果: $result';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Rust API 调用示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _callGetSyncResult,
child: const Text('调用 getSyncResult'),
)
],
),
),
);
}
}
在flutter_rust_bridge
里,flutter和原生都可以调用 这个已经集成了库,直接引入头文件然后调用即可