Post

在原生swift/java/fluuter里调用rust的库

在原生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
    }

image —-

在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)),
        ),
      ),
    );
  }
}

image

image

问题: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

image


手动使用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和原生都可以调用 这个已经集成了库,直接引入头文件然后调用即可

This post is licensed under CC BY 4.0 by the author.