[dart:ffi笔记]dart:ffi同步/异步调用指南

3,931 阅读1分钟

示例:github.com/Sunbreak/na…

Dart --> Native

Dart同步调用Native方法

Dart FFI函数的声明

// Declaration
final int Function(int x, int y) nativeAdd = nativeInteropLib
    .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add")
    .asFunction();

Dart FFI函数的使用

// Usage
print('nativeAdd(1, 2) = ${nativeAdd(1, 2)}');

FFI函数的Native实现

Native方法需要暴露C接口

// Implementation
__attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t a, int32_t b) {
    printf("native_add called\n"); // XCode debug print
    return a + b;
}

Native --> Dart

Dart同步调用Native方法,并Native同步回调Dart方法

Dart FFI函数的声明

// Declaration
typedef NativeSyncCallbackFunc = Int32 Function(Int32 n);

typedef _c_NativeSyncCallback = Void Function(
  Pointer<NativeFunction<NativeSyncCallbackFunc>> callback,
);

typedef _dart_NativeSyncCallback = void Function(
  Pointer<NativeFunction<NativeSyncCallbackFunc>> callback,
);

final _dart_NativeSyncCallback nativeSyncCallback = nativeInteropLib
    .lookup<NativeFunction<_c_NativeSyncCallback>>("NativeSyncCallback")
    .asFunction();

Dart回调函数的声明

Dart回调函数需要是top-level函数

// Declaration
int normalSyncCallback(int n) {
  print('normalSyncCallback called');
  return n * n;
}

Dart FFI函数的使用

// Usage
var normalFunc = Pointer.fromFunction<NativeSyncCallbackFunc>(normalSyncCallback, syncExceptionalReturn);
nativeSyncCallback(normalFunc);

FFI函数的Native实现

C++实现需要暴露C接口

// Implementation
#ifdef __cplusplus
extern "C" {
#endif

typedef int32_t (*CallbackFunc)(int32_t n);

__attribute__((visibility("default"))) __attribute__((used))
void NativeSyncCallback(CallbackFunc callback) {
    std::cout << "NativeSyncCallback callback(9) = " << callback(9) << std::endl; // XCode debug print
}

#ifdef __cplusplus
}
#endif

Native -async-> Dart

Dart同步调用Native方法,并Native异步回调Dart方法

Dart FFI函数的声明

// Declar
typedef NativeAsyncCallbackFunc = Void Function();

typedef _c_NativeAsyncCallback = Void Function(
  Pointer<NativeFunction<NativeAsyncCallbackFunc>> callback,
);

typedef _dart_NativeAsyncCallback = void Function(
  Pointer<NativeFunction<NativeAsyncCallbackFunc>> callback,
);

final _dart_NativeAsyncCallback nativeAsyncCallback = nativeInteropLib
    .lookup<NativeFunction<_c_NativeAsyncCallback>>("NativeAsyncCallback")
    .asFunction();

Dart回调函数的声明

Dart回调函数需要是top-level函数

// Declaration
void asyncCallback() {
  print('asyncCallback called');
}

Dart FFI函数的使用

// Usage
var asyncFunc = Pointer.fromFunction<NativeAsyncCallbackFunc>(asyncCallback);
nativeAsyncCallback(asyncFunc);

FFI函数的Native实现

Dart回调函数执行需要在发起转换(即fromFunction)的Dart Isolate的Mutator Thread执行,所以必须在Native侧,把Dart回调函数指针通过SendPort发回Mutator Thread,然后转换执行

参考代码:github.com/dart-lang/s…

初始化Dart Dynamic Library API

  • Dart声明
// Declaration
final _initializeApi = nativeInteropLib.lookupFunction<
    IntPtr Function(Pointer<Void>),
    int Function(Pointer<Void>)>("InitDartApiDL");
  • Dart使用
// Usage
WidgetsFlutterBinding.ensureInitialized();
var nativeInited = _initializeApi(NativeApi.initializeApiDLData);
  • Native声明

来自github.com/dart-lang/s…

// Declaration
DART_EXTERN intptr_t Dart_InitializeApiDL(void* data);

注册SendPort

  • Dart声明
// Declaration
final _registerSendPort = nativeInteropLib.lookupFunction<
    Void Function(Int64 sendPort),
    void Function(int sendPort)>('RegisterSendPort');
  • Dart使用
// Usage
_registerSendPort(_receivePort.sendPort.nativePort);
  • Native实现
// Implementaion
Dart_Port send_port_;

DART_EXPORT void RegisterSendPort(Dart_Port send_port) {
  send_port_ = send_port;
}

Dart FFI函数的Native实现

// Implementation
DART_EXPORT void NativeAsyncCallback(VoidCallbackFunc callback) {
    printf("NativeAsyncCallback Running on (%p)\n", pthread_self());
    
    pthread_t callback_thread;
    pthread_create(&callback_thread, NULL, thread_func, (void *)callback);
}

Native发送回调函数指针给Dart

void *thread_func(void *args) {
    printf("thread_func Running on (%p)\n", pthread_self());
    sleep(1 /* seconds */); // doing something

    Dart_CObject dart_object;
    dart_object.type = Dart_CObject_kInt64;
    dart_object.value.as_int64 = reinterpret_cast<intptr_t>(args);
    Dart_PostCObject_DL(send_port_, &dart_object);

    pthread_exit(args);
}

Dart接收回调函数指针

void _handleNativeMessage(dynamic message) {
  print('_handleNativeMessage $message');
  final int address = message;
  _executeCallback(Pointer<Void>.fromAddress(address).cast());
}

执行回调函数

因为函数指针为Native指针,目前无法还原为Dart函数,需要传递到Native侧执行

  • Dart声明
// Declaration
final _executeCallback = nativeInteropLib.lookupFunction<_c_NativeAsyncCallback,
    _dart_NativeAsyncCallback>('ExecuteCallback');
  • Dart使用
// Usage
_executeCallback(Pointer<Void>.fromAddress(address).cast());
  • Native实现
// Implementaion
DART_EXPORT void ExecuteCallback(VoidCallbackFunc callback) {
    printf("ExecuteCallback Running on (%p)\n", pthread_self());
    callback();
}

可以看到NativeAsyncCallback和ExecuteCallback是在同一线程,即Dart Isolate的Mutator线程,而thread_func执行在另一个线程

Native -messge-> Dart

一般情况下,更多使用message的方式给Dart侧传递消息,让Dart侧自行决定回调的处理方式,类似Flutter的MessageChannel

参考代码:github.com/dart-lang/s…

  • 消息定义
// Declaration
class CppResponse {
  final int pendingCall;
  final Uint8List data;

  CppResponse(this.pendingCall, this.data);

  List toCppMessage() => List.from([pendingCall, data], growable: false);

  String toString() => 'CppResponse(message: ${data.length})';
}
  • 消息使用
void handleCppRequests(dynamic message) {
  final cppRequest = CppRequest.fromCppMessage(message);
  print('Dart:   Got message: $cppRequest');

  if (cppRequest.method == 'myCallback1') {
    // Use the data in any way you like. Here we just take the first byte as
    // the argument to the function.
    final int argument = cppRequest.data[0];
    final int result = myCallback1(argument);
    final cppResponse =
        CppResponse(cppRequest.pendingCall!, Uint8List.fromList([result]));
    print('Dart:   Responding: $cppResponse');
    cppRequest.replyPort!.send(cppResponse.toCppMessage());
  } else if (cppRequest.method == 'myCallback2') {
    final int argument = cppRequest.data[0];
    myCallback2(argument);
  }
}