Flutter(Dart)ffi-在处理外部库方法期间应用程序冻结

ecbunoof  于 2022-12-06  发布在  Flutter
关注(0)|答案(2)|浏览(241)

我正在使用C库iperf3来测量网络。当我开始网络测试时,我的应用程序冻结并等待结果。我尝试了异步和线程,但没有任何进展。有什么建议吗?我想运行我的测试并异步调用另一个方法(最好再次调用此库,但调用其他方法)。可能吗?
我的网络.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
}
kknvjkwl

kknvjkwl1#

Async Callbacks

The problem is that C routines called from dart are blocking and therefore congest the single existing dart isolate, consequently freezing the UI.
To work around this problem you have to open a port on the dart isolate through which your C routines can asynchronously send messages to the dart isolate. To signal to the dart compiler that this is a non-blocking operation, simply delay the completion of the function until a message on the designated port has been received.

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;
}

In C, you need to create a trigger function which should ideally be as lightweight as possible, passing the port number to your C code and calling the actual function you want to execute on a different thread. The trigger function will finish almost instantly, allowing your dart thread to do other work and as soon as the newly created thread is done, it sends its result through the native port back to the dart isolate which can pick up where it left off.

void native_trigger_function(Dart_Port port) {
  pthread_t t;
  Dart_Port *args = (Dart_Port *) malloc(sizeof(Dart_Port));
  args = &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(port, &obj);
  
  free(args);
  pthread_exit(NULL);
}

Note: This logic relies on the native dart api to work which can be found here . Before use, the interface needs to be attached to the current dart isolate which can be achieved by calling Dart_InitializeApiDL(dart_api_data) from C where dart_api_data is a void pointer which can be obtained from your dart code using the dart:ffi package through NativeApi.initializeApiData .
**Update:** Thanks @fdollack for fixing the example snippets!

cedebl8k

cedebl8k2#

谢谢你@卢卡斯·阿申巴赫!这个最小的例子太难找到了。
2个小的添加。首先,分配的指针应该被强制转换到(Dart_Port*),并且dart的端口参数必须被赋值/复制到指针所在的位置!

void native_trigger_function(Dart_Port port) {
  pthread_t t;
  Dart_Port *args= (Dart_Port*)malloc(sizeof(Dart_Port));
  *args = port; // assign port
  pthread_create(&t, NULL, _native_function, args);
}

第二件事是在_native_function中对Dart的响应必须是

Dart_PostCObject_DL(port, &obj);

代替

Dart_PostCObject_DL(args_c.send_port, &obj);

相关问题