Flutter (Dart) ffi - 应用程序在处理外部库方法期间冻结
Flutter (Dart) ffi - Aplication freezes during processing external library methohd
我正在使用 C 库 iperf3 测量网络。当我开始网络测试时,我的应用程序冻结并等待结果。我尝试了异步和线程,但没有任何进展。有什么建议吗?我想 运行 我的测试并异步调用另一个方法(最好再次调用这个库,但其他方法)。可能吗?
我的network.dart
final DynamicLibrary iperfLib = Platform.isAndroid
? DynamicLibrary.open("libiperf.so")
: DynamicLibrary.process();
typedef RunTestFunc = ffi.Pointer<ffi.Uint8> Function(
ffi.Pointer<ffi.Uint8> context);
typedef RunTest = ffi.Pointer<ffi.Uint8> Function(
ffi.Pointer<ffi.Uint8> context);
RunTest _run_test = iperfLib
.lookup<ffi.NativeFunction<RunTestFunc>>('run_test')
.asFunction<RunTest>();
ffi.Pointer<ffi.Uint8> runTest(ffi.Pointer<ffi.Uint8> context) {
return _run_test(context);
}
和iperf.c
Iperf* run_test(Iperf* test) {
__android_log_print( ANDROID_LOG_INFO, "DONE ", "server_hostname %s", test->server_hostname );
int cc = iperf_run_client( test ) ;
__android_log_print( ANDROID_LOG_INFO, "DONE ", " %d",cc );
iperf_free_test( test );
return test
}
异步回调
问题是从 dart 调用的 C 例程是阻塞的,因此会阻塞单个现有的 dart 隔离,从而冻结 UI。
要解决此问题,您必须在 dart isolate 上打开一个端口,您的 C 例程可以通过该端口向 dart isolate 异步发送消息。要向 dart 编译器发出这是非阻塞操作的信号,只需延迟函数的完成,直到在指定端口上收到消息。
Future<int> asyncData() async {
var receiveData;
bool receivedCallback = false;
var receivePort = ReceivePort()..listen((data) {
print('Received data from c');
receiveData = data;
receivedCallback = true;
});
var nativeSendPort = receivePort.sendPort.nativePort;
nativeTriggerFunction(nativeSendPort);
while(!receivedCallback) {
await Future.delayed(Duration(milliseconds: 100));
}
receivePort.close();
return receiveData;
}
在 C 中,您需要创建一个触发器函数,理想情况下它应该尽可能轻量级,将端口号传递给您的 C 代码并调用您要在不同线程上执行的实际函数。
触发器函数几乎会立即完成,允许您的 dart 线程执行其他工作,并且一旦新创建的线程完成,它就会通过本机端口将其结果发送回 dart isolate,后者可以从它停止的地方继续。
void native_trigger_function(Dart_Port port) {
pthread_t t;
Dart_Port *args= malloc(sizeof(Dart_Port));
pthread_create(&t, NULL, _native_function, args);
}
void *_native_function(void *args) {
Dart_Port port = *(Dart_Port *)args;
int rc = 0;
// do some heavy work
// send return code to dart
Dart_CObject obj;
obj.type = Dart_CObject_kInt32;
obj.value.as_int32 = rc;
Dart_PostCObject_DL(args_c.send_port, &obj);
free(args);
pthread_exit(NULL);
}
注意: 此逻辑依赖于原生 dart api 才能工作,可以找到 here。在使用之前,接口需要附加到当前的 dart 隔离,这可以通过从 C 调用 Dart_InitializeApiDL(dart_api_data)
来实现,其中 dart_api_data
是一个空指针,可以使用 [=29 从你的 dart 代码中获得=] 通过 NativeApi.initializeApiData
.
打包
我正在使用 C 库 iperf3 测量网络。当我开始网络测试时,我的应用程序冻结并等待结果。我尝试了异步和线程,但没有任何进展。有什么建议吗?我想 运行 我的测试并异步调用另一个方法(最好再次调用这个库,但其他方法)。可能吗?
我的network.dart
final DynamicLibrary iperfLib = Platform.isAndroid
? DynamicLibrary.open("libiperf.so")
: DynamicLibrary.process();
typedef RunTestFunc = ffi.Pointer<ffi.Uint8> Function(
ffi.Pointer<ffi.Uint8> context);
typedef RunTest = ffi.Pointer<ffi.Uint8> Function(
ffi.Pointer<ffi.Uint8> context);
RunTest _run_test = iperfLib
.lookup<ffi.NativeFunction<RunTestFunc>>('run_test')
.asFunction<RunTest>();
ffi.Pointer<ffi.Uint8> runTest(ffi.Pointer<ffi.Uint8> context) {
return _run_test(context);
}
和iperf.c
Iperf* run_test(Iperf* test) {
__android_log_print( ANDROID_LOG_INFO, "DONE ", "server_hostname %s", test->server_hostname );
int cc = iperf_run_client( test ) ;
__android_log_print( ANDROID_LOG_INFO, "DONE ", " %d",cc );
iperf_free_test( test );
return test
}
异步回调
问题是从 dart 调用的 C 例程是阻塞的,因此会阻塞单个现有的 dart 隔离,从而冻结 UI。
要解决此问题,您必须在 dart isolate 上打开一个端口,您的 C 例程可以通过该端口向 dart isolate 异步发送消息。要向 dart 编译器发出这是非阻塞操作的信号,只需延迟函数的完成,直到在指定端口上收到消息。
Future<int> asyncData() async {
var receiveData;
bool receivedCallback = false;
var receivePort = ReceivePort()..listen((data) {
print('Received data from c');
receiveData = data;
receivedCallback = true;
});
var nativeSendPort = receivePort.sendPort.nativePort;
nativeTriggerFunction(nativeSendPort);
while(!receivedCallback) {
await Future.delayed(Duration(milliseconds: 100));
}
receivePort.close();
return receiveData;
}
在 C 中,您需要创建一个触发器函数,理想情况下它应该尽可能轻量级,将端口号传递给您的 C 代码并调用您要在不同线程上执行的实际函数。 触发器函数几乎会立即完成,允许您的 dart 线程执行其他工作,并且一旦新创建的线程完成,它就会通过本机端口将其结果发送回 dart isolate,后者可以从它停止的地方继续。
void native_trigger_function(Dart_Port port) {
pthread_t t;
Dart_Port *args= malloc(sizeof(Dart_Port));
pthread_create(&t, NULL, _native_function, args);
}
void *_native_function(void *args) {
Dart_Port port = *(Dart_Port *)args;
int rc = 0;
// do some heavy work
// send return code to dart
Dart_CObject obj;
obj.type = Dart_CObject_kInt32;
obj.value.as_int32 = rc;
Dart_PostCObject_DL(args_c.send_port, &obj);
free(args);
pthread_exit(NULL);
}
注意: 此逻辑依赖于原生 dart api 才能工作,可以找到 here。在使用之前,接口需要附加到当前的 dart 隔离,这可以通过从 C 调用 Dart_InitializeApiDL(dart_api_data)
来实现,其中 dart_api_data
是一个空指针,可以使用 [=29 从你的 dart 代码中获得=] 通过 NativeApi.initializeApiData
.