如何通过dart:ffi调用C++ .so动态库内对象的成员函数?
核心错误原因
你查找符号的逻辑从根上就错了:
- 动态链接库(.so)导出的可供查找的符号,全部是符合C ABI的全局函数,不存在
对象名.方法名格式的符号。g.sayHello是Java/Kotlin层面向对象语法的调用写法,编译后根本不会以这个名字存在于.so的符号表里,自然会报undefined symbol。 - 你定义的函数签名和实际方法完全不匹配:
sayHello接收一个字符串入参、返回字符串,你写的typedef是无参函数,就算找到符号调用也会崩溃。
解决步骤
1. 先确认.so内真实导出的符号名
把对应ABI架构的.so文件拉到本地,执行如下命令查看所有和greet/sayHello相关的导出符号:
nm -D libGreetings.so | grep -i -E "(sayhello|greet)"
命令输出的函数名才是你可以传入lookupFunction的合法符号名。
注意:如果你的.so是仅做了JNI适配的版本,查出来的符号会是
Java_<包名>_RustGreetings_sayHello格式,这类是专门给Java虚拟机调用的JNI函数,强依赖JNI环境,不能直接用Dart FFI调用,要么重新编译Rust库增加C ABI导出,要么走Flutter的MethodChannel调安卓原生层逻辑,不要直接lookup这类符号。
2. 修正函数签名定义
按照入参、出参都是字符串的实际逻辑,修正类型定义:
// C侧函数签名:入参为Utf8字符串指针,返回Utf8字符串指针 typedef SayHelloC = Pointer<Utf8> Function(Pointer<Utf8> content); // Dart侧对应函数签名 typedef SayHelloDart = Pointer<Utf8> Function(Pointer<Utf8> content);
3. 正确加载、查找、调用函数
如果你的.so已经导出了C ABI兼容的全局函数(Rust侧一般会加#[no_mangle] pub extern "C"标记导出,比如导出的函数名是say_hello,替换成你第一步查出来的实际符号名即可),按如下逻辑调用:
// 动态库加载逻辑保持原有逻辑即可 final lib = Platform.isAndroid ? DynamicLibrary.open(libGreetings) : DynamicLibrary.process(); // 用真实导出的符号名查找函数 final sayHello = lib.lookupFunction<SayHelloC, SayHelloDart>('say_hello'); // 执行调用,注意原生内存的申请和释放 final input = "world".toNativeUtf8(); try { final resPtr = sayHello(input); final result = resPtr.toDartString(); print("调用结果:$result"); // 提示:如果返回的字符串是Rust侧在堆上分配的,需要调用Rust侧导出的释放函数回收内存,否则会造成内存泄漏 } catch (e) { print("调用出错:${e.toString()}"); } finally { // 释放入参占用的原生内存 calloc.free(input); }
常见坑点
- Rust代码如果没加
#[no_mangle]和extern "C"标记,编译后会被符号混淆,你查不到可读的函数名,必须重新编译库加上标记。 - 不要尝试直接调用JNI格式的导出函数,这类函数要求第一个参数是JNI环境指针、第二个参数是类/对象实例,直接从Dart传参不符合调用约定,会直接触发内存访问错误崩溃。
内容的提问来源于stack exchange,提问作者C ivit Vaghani
相关产品推荐
相关产品推荐

