Flutter调用C语言(FFI方式)

背景

在做嵌入式Linux时选用了Flutter做GUI,但是底层必然使用C写的,那么最终怎么交互呢?这里调研了FFI,FFI赋予了Dart语言调用.so动态链接库的能力。
虽然项目后来采用了Dbus进行通信,还是记录下研究过程吧。

Flutter侧

这个方法应该是跟JNI非常相似的
调用流程:

  1. 从FFI角度和Dart角度分别定义函数类型:
typedef HelloWorldFunc = ffi.Void Function(); 	
typedef HelloWorld = void Function();
// 带参数
typedef HelloWorldFunc = ffi.Int32 Function(ffi.Int32 param);
typedef HelloWorld = int Function(int param);
  1. 打开链接库
 ffi.DynamicLibrary.open(libraryPath);
  1. 声明dart函数并关联到C侧函数
final HelloWorld hello = dylib
    .lookup<ffi.NativeFunction<HelloWorldFunc>>('hello_world')
    .asFunction();
  1. 愉快的调用
void test_C() {
  print("test_C called  ");
  hello();
}

消息传递

  1. 函数返回值传递
  2. SendPort传递

重点讲一下SendPort传递消息吧,因为dart是线程隔离的,如果你在C语言侧起了新的的线程执行了一些任务并且想把结果传回dart,就要用到这个了。这里一定注意内存泄漏!!!

C语言代码


static void* test_thread_send_string(void *arg);
static void* test_thread_send_struct(void *arg);
static void* test_thread_send_list(void *arg);

static void FreeFinalizer(void* v, void* value);

int test(Dart_Port send_port_){
    send_port = send_port_;
    pthread_t thread_string;
    pthread_t thread_struct;
    pthread_t thread_list;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&thread_string, NULL, test_thread_send_string, NULL);

    pthread_create(&thread_struct, NULL, test_thread_send_struct, NULL);

    pthread_create(&thread_list, NULL, test_thread_send_list, NULL);


    return 0;
}



void * test_thread_send_string(void *arg){

    const char* testMessage = "This is a test message from C";

    Dart_CObject c_test_message;
    c_test_message.type = Dart_CObject_kString;
    c_test_message.value.as_string = (char*)(testMessage);
    Dart_PostCObject_DL(send_port, &c_test_message);

    pthread_exit(NULL);
}

void * test_thread_send_struct(void *arg){
    typedef struct {
        int num;
        char *text_1;
        char text_2[1000];
    } TestMessageStruct;
    TestMessageStruct * testMessage = (TestMessageStruct *)malloc(sizeof(TestMessageStruct));
    testMessage->num = 123;
    testMessage->text_1 = "This is a test message structure from C";
    sprintf(testMessage->text_2,"This is a test message structure from C");

    char * p = testMessage->text_1;
    Dart_CObject c_data;
    c_data.type = Dart_CObject_kInt64;
    c_data.value.as_int64 = (intptr_t)(testMessage);
    printf("befor sleep 10s  testMessageStruct->num:%d testMessage->text_1:%s \n",testMessage->num,testMessage->text_1);
    Dart_PostCObject_DL(send_port, &c_data);
    sleep(10);
    // Becasue the testMessage have been free in the dart side ,it will print a random number or null. 
    // But it can't free the memary of text_1 , it is recommend that use the way of text_2 when you are designing the struct.
    printf("after sleep 10s  testMessageStruct->num:%d testMessage->text_1:%s \n",testMessage->num,p);
    pthread_exit(NULL);
}
void * test_thread_send_list(void *arg){
    
    // printf("test_thread_send_list \n");
    typedef struct {
        int num;
        char * text;
    } TestMessageStruct;
    TestMessageStruct * testMessageStruct = (TestMessageStruct *)malloc(sizeof(TestMessageStruct));
    testMessageStruct->num = 123;
    testMessageStruct->text = "This is a test message struct from C";
    Dart_CObject c_data;
    c_data.type = Dart_CObject_kInt64;
    c_data.value.as_int64 = (intptr_t)(testMessageStruct);


    const char* testMessage = "This is a test message list[1] from C";
    Dart_CObject c_test_message;
    c_test_message.type = Dart_CObject_kString;
    c_test_message.value.as_string = (char*)(testMessage);



    Dart_CObject* c_list_arr[] = {&c_data, &c_test_message, &c_data};
    Dart_CObject c_list;
    c_list.type = Dart_CObject_kArray;
    c_list.value.as_array.values = c_list_arr;
    c_list.value.as_array.length = sizeof(c_list_arr) / sizeof(c_list_arr[0]);

    Dart_PostCObject_DL(send_port, &c_list);

    pthread_exit(NULL);
}




static void FreeFinalizer(void* v, void* value){
  free(value);
}

intptr_t InitDartApiDL(void* data) {
  return Dart_InitializeApiDL(data);
}

Flutter

// 定义消息handler函数



typedef MessageHandler = void Function(dynamic msg);

MessageHandler messageHandlerAsync = (dynamic msg) {
  print(msg.runtimeType);

  // string test
  if (msg.runtimeType.toString() == "String") {
    String stringMsg = msg;
    print("messageHandlerAsync called : $stringMsg");
  }
  // struct test
  // final msgAddress = msg as int;
  if (msg.runtimeType == int) {
    print("int type as an address of struct");
    TestMessageStruct testMessageStruct =
        Pointer<TestMessageStruct>.fromAddress(msg).ref;
    print("messageHandlerAsync called : ${testMessageStruct.num}");
	// 实际传过来的地址,一定free,避免内存泄露
    malloc.free(Pointer<TestMessageStruct>.fromAddress(msg));
  }
  // list test
  if (msg.runtimeType == List<dynamic>) {
    print('list test :${msg[1]}');
  }
};
// 使用时注册监听即可
void main() {
  initializeApi(NativeApi.initializeApiDLData);
  final interactiveCRequests = ReceivePort()..listen(messageHandler);
  final int nativePort = interactiveCRequests.sendPort.nativePort;
  test(messageHandlerAsync);
}


你可能感兴趣的:(flutter,c语言,dart,FFI)