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

问题描述

我正在使用 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," %d",cc );
    iperf_free_test( test );
    return test
}

解决方法

异步回调

问题是从 dart 调用的 C 例程会阻塞,因此会阻塞现有的单个 dart 隔离,从而冻结 UI。

要解决此问题,您必须在 dart 隔离上打开一个端口,您的 C 例程可以通过该端口向 dart 隔离异步发送消息。要通知 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 隔离,后者可以从停止的地方继续。

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 是一个空指针,可以使用 dart:ffi 从您的 dart 代码中获取通过 NativeApi.initializeApiData 打包。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...