dart-ffi 原理

1,273 阅读1分钟

dart-ffi原理

  • ffi通过将dartVM提供的符号表绑定到其提供的头文件的方法中。我们引入dartVM提供的头文件,就可以与dartVM通信。
  • DynamicLibrary.process/DynamicLibrary.open会加载并解析动态库的符号表,并提供lookupFunction查找我们提供的函数地址,通过字符串映射的方式,将dart的函数地址与native函数地址绑定。
  • 一般而言,我们提供的函数,在android上会被打包成so文件,如libhy_ffi.so,ios会被打包成Mach-O文件

ios_mach_o.png

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_ffi_dart_head.png

ios绑定失败问题

  • ios xcode默认情况下线上编译会关闭动态符号输出,导致在绑定dartVM符号表时,出现失败。

ios_ffi_setting.png

  • 默认情况下stripMode会选择AllSymbols,这样用
extern "C" __attribute__((visibility("default"))) __attribute__((used))

标记的方法不会暴露到符号表中,因此也无法被绑定到。需要修改为none-global symbols.

  • 我们可以通过命令查看导出到符号表的函数名
objdump -t | grep InitializeChannel

ios_ffi_setting_symbols.png