dart-ffi原理
- ffi通过将dartVM提供的符号表绑定到其提供的头文件的方法中。我们引入dartVM提供的头文件,就可以与dartVM通信。
- DynamicLibrary.process/DynamicLibrary.open会加载并解析动态库的符号表,并提供lookupFunction查找我们提供的函数地址,通过字符串映射的方式,将dart的函数地址与native函数地址绑定。
- 一般而言,我们提供的函数,在android上会被打包成so文件,如libhy_ffi.so,ios会被打包成Mach-O文件
dart侧
final DynamicLibrary ffiLib = Platform.isAndroid
? DynamicLibrary.open('libhy_ffi.so')
: DynamicLibrary.process();
final initializeChannel = ffiLib.lookupFunction<
Void Function(Pointer<Void>, Int64, Int64),
void Function(Pointer<Void>, int, int)>("InitializeChannel");
void init(){
initializeChannel(
NativeApi.initializeApiDLData,
_replyPort.sendPort.nativePort,
_messagePort.sendPort.nativePort,
);
...
}
android侧
FFI_EXPORT
void InitializeChannel(void *data, int64_t replyPort, int64_t messagePort) {
Dart_InitializeApiDL(data);
replyPort_ = replyPort;
messagePort_ = messagePort;
...
}
ios 侧
extern "C" __attribute__((visibility("default"))) __attribute__((used))
void InitializeChannel(void *data, int64_t replyPort, int64_t messagePort) {
Dart_InitializeApiDL(data);
replyPort_ = replyPort;
messagePort_ = messagePort;
}
- DynamicLibrary通过字符串查找,找到我们提供的动态库中的函数InitializeChannel,执行dartVM的初始化方法,
- 初始化方法传递的参数主要包含dartVM提供的符号表信息,同时提供两个端口回调端口和消息端口接受来自native发往dartVM的信息。
- native侧触发初始化InitializeChannel时,将dartVM提供的头文件中的方法名与initializeApiDLData中符号表中的地址绑定。
- native侧即可通过dartVM提供的头文件中的方法与dartVM通信。
dartVM提供的头文件
ios绑定失败问题
- ios xcode默认情况下线上编译会关闭动态符号输出,导致在绑定dartVM符号表时,出现失败。
- 默认情况下stripMode会选择AllSymbols,这样用
extern "C" __attribute__((visibility("default"))) __attribute__((used))
标记的方法不会暴露到符号表中,因此也无法被绑定到。需要修改为none-global symbols.
- 我们可以通过命令查看导出到符号表的函数名
objdump -t | grep InitializeChannel