flutter/dart通过ffi调用rust代码
ffi简介 FFI(Foreign Function Interface)是用来与其它语言交互的接口,在有些语言里面称为语言绑定(language bindings),Java 里面一般称为 JNI(Java Native Interface) 或 JNA(Java Native Access)。由于现实中很多程序是由不同编程语言写的,必然会涉及到跨语言调用,比如 A 语言写的函数如果想在 B 语言里面调用,这时一般有两种解决方案:一种是将函数做成一个服务,通过进程间通信(IPC)或网络协议通信(RPC, RESTFul等);另一种就是直接通过 FFI 调用。前者需要至少两个独立的进程才能实现,而后者直接将其它语言的接口内嵌到本语言中,所以调用效率比前者高。
rust代码
- 创建一个文件夹,如flutter-ffi-rust,然后进入flutter-ffi-rust文件夹执行:cargo new rust_ffi
- 进入rust_ffi执行:cargo run,确保创建的rust项目可以执行
- 在Cargo.toml文件中添加代码如下:
[lib] name = "casher" crate-type = ["cdylib"]
- 将mian.rs改为lib.rs,将如下代码粘贴进去:
use std::io::Write; use std::ffi::CStr; use std::os::raw::c_char; #[no_mangle] pub extern "C" fn play_once(ptr: *const c_char) -> u32 { let cstr = unsafe { CStr::from_ptr(ptr)}; let a = cstr.to_str().unwrap(); let mut file = std::fs::File::create("data.txt").expect("create failed"); file.write_all("简单测试121212".as_bytes()).expect("write failed"); file.write_all(" 简单编程".as_bytes()).expect("write failed"); println!("data written to file" ); println!("data written to file----{}",a); let return_num = 10; return_num } #[no_mangle] pub extern "C" fn play_once_test(ptr: *const c_char) -> u32 { let cstr = unsafe { CStr::from_ptr(ptr)}; let a = cstr.to_str().unwrap(); println!("data written to file-----------{}",a); let mut file = std::fs::File::create("datatest.txt").expect("create failed"); file.write_all("简单教程测试".as_bytes()).expect("write failed"); file.write_all(" 简单编程测试".as_bytes()).expect("write failed"); println!("data written to file" ); let s = 11; s }
- 执行cargo build,然后target/debug下面会出现一个ibcasher.dylib文件,注意:在Linux上会创建*.so文件,在MacOSX上会创建*.dylib文件,在Windows上会创建*.dll文件,我是macos,所以会生成dylib文件
dart代码
- 在flutter-ffi-rust文件夹下创建flutter项目,执行dart create flutter_ffi
- 进入flutter_ffi里面执行dart run,确保创建的flutter项目正确,然后执行 dart pub add ffi 或者在pubspec.yaml 安装 ffi: ^1.2.1
- 将如下代码粘入bin/flutter_ffi.dart里面:
import dart:ffi as ffi; import package:ffi/ffi.dart; typedef playOnceFunc = ffi.Void Function(ffi.Pointer<Utf8>); typedef PlayOnce = void Function(ffi.Pointer<Utf8>); void main() { final ffi.DynamicLibrary dylib = ffi.DynamicLibrary.open(libcasher.dylib); // final PlayOnce play_once = dylib // .lookup<ffi.NativeFunction<playOnceFunc>>(play_once) // .asFunction(); // final PlayOnce play_once_test = dylib // .lookup<ffi.NativeFunction<playOnceFunc>>(play_once_test) // .asFunction(); const String file_name = 用来测试传递参数; // Convert a Dart [String] to a Utf8-encoded null-terminated C string. final ffi.Pointer<Utf8> charPointer = file_name.toNativeUtf8(); var result = dylib.lookupFunction<ffi.Pointer<Utf8> Function(ffi.Pointer<Utf8>),ffi.Pointer<Utf8> Function(ffi.Pointer<Utf8>)>(play_once_test,isLeaf:true); // play_once(charPointer); var s = result(charPointer).address; print("=============="); print(s); // play_once_test(); }
- 将生成的*.dylib文件放入flutter_ffi文件夹下
- 执行dart run,看调用rust的结果,会生成data.txt文件并写入数据