Example: github.com/Sunbreak/na…
Dart –> Native
Dart calls Native methods synchronously
Dart FFI function declaration
// Declaration
final int Function(int x, int y) nativeAdd = nativeInteropLib
.lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add")
.asFunction();
Copy the code
Use of the Dart FFI function
// Usage
print('nativeAdd(1, 2) = ${nativeAdd(1.2)}');
Copy the code
Native implementation of FFI functions
Native methods need to expose the C interface
// 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;
}
Copy the code
Native –> Dart
Dart synchronously invokes Native methods and Native synchronously invokes Dart methods
Dart FFI function declaration
// 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();
Copy the code
Dart callback function declaration
The Dart callback needs to be a top-level function
// Declaration
int normalSyncCallback(int n) {
print('normalSyncCallback called');
return n * n;
}
Copy the code
Use of the Dart FFI function
// Usage
var normalFunc = Pointer.fromFunction<NativeSyncCallbackFunc>(normalSyncCallback, syncExceptionalReturn);
nativeSyncCallback(normalFunc);
Copy the code
Native implementation of FFI functions
C++ implementations need to expose C interfaces
// 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
Copy the code
Native -async-> Dart
Dart calls Native methods synchronously and Native calls back Dart methods asynchronously
Dart FFI function declaration
// Declar
typedef NativeAsyncCallbackFunc = Void Function(a);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();
Copy the code
Dart callback function declaration
The Dart callback needs to be a top-level function
// Declaration
void asyncCallback() {
print('asyncCallback called');
}
Copy the code
Use of the Dart FFI function
// Usage
var asyncFunc = Pointer.fromFunction<NativeAsyncCallbackFunc>(asyncCallback);
nativeAsyncCallback(asyncFunc);
Copy the code
Native implementation of FFI functions
Dart callback function execution needs to be performed on the Mutator Thread of the Dart Isolate that initiated the transformation (i.e., fromFunction). Therefore, Dart callback function pointer must be sent back to the Mutator Thread via SendPort on the Native side to execute the transformation
Reference code: github.com/dart-lang/s…
Initialize the Dart Dynamic Library API
- The Dart statement
// Declaration
final _initializeApi = nativeInteropLib.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Copy the code
- Use the Dart
// Usage
WidgetsFlutterBinding.ensureInitialized();
var nativeInited = _initializeApi(NativeApi.initializeApiDLData);
Copy the code
- Native statement
From github.com/dart-lang/s…
// Declaration
DART_EXTERN intptr_t Dart_InitializeApiDL(void* data);
Copy the code
Registered SendPort
- The Dart statement
// Declaration
final _registerSendPort = nativeInteropLib.lookupFunction<
Void Function(Int64 sendPort),
void Function(int sendPort)>('RegisterSendPort');
Copy the code
- Use the Dart
// Usage
_registerSendPort(_receivePort.sendPort.nativePort);
Copy the code
- Native implementation
// Implementaion
Dart_Port send_port_;
DART_EXPORT void RegisterSendPort(Dart_Port send_port) {
send_port_ = send_port;
}
Copy the code
Native implementation of Dart FFI functions
// 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);
}
Copy the code
Native sends the callback function pointer to 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);
}
Copy the code
Dart receives the callback function pointer
void _handleNativeMessage(dynamic message) {
print('_handleNativeMessage $message');
final int address = message;
_executeCallback(Pointer<Void>.fromAddress(address).cast());
}
Copy the code
Execute the callback function
Because function pointer is Native pointer, it cannot be restored to Dart function at present and needs to be transferred to Native side for execution
- The Dart statement
// Declaration
final _executeCallback = nativeInteropLib.lookupFunction<_c_NativeAsyncCallback,
_dart_NativeAsyncCallback>('ExecuteCallback');
Copy the code
- Use the Dart
// Usage
_executeCallback(Pointer<Void>.fromAddress(address).cast());
Copy the code
- Native implementation
// Implementaion
DART_EXPORT void ExecuteCallback(VoidCallbackFunc callback) {
printf("ExecuteCallback Running on (%p)\n".pthread_self());
callback(a); }Copy the code
You can see that NativeAsyncCallback and ExecuteCallback are executed on the same thread, the Mutator thread of the Dart Isolate, while thread_func is executed on another thread
Native -messge-> Dart
Generally, messages are sent to the Dart side in a message manner and the Dart side decides how to handle callbacks, similar to the MessageChannel of Flutter
Reference code: github.com/dart-lang/s…
- The message definition
// 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}) ';
}
Copy the code
- message
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);
finalcppResponse = 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); }}Copy the code