Flutter FFI 学习笔记系列
《Flutter FFI 最简示例》《Flutter FFI 基础数据类型》《Flutter FFI 函数》《Flutter FFI 字符串》《Flutter FFI 结构体》《Flutter FFI 类》《Flutter FFI 数组》《Flutter FFI 内存管理》《Flutter FFI Dart Native API》
在前面的章节中,演示了如何在 Dart 中访问 C 中的函数。接下来将详细介绍 C 和 Dart 函数的相互调用。
1、Dart 调用 C 函数
1.1 C函数的定义
Dart 语言只能调用 C 语言风格的函数,不能调用 C++ 语言风格的函数,因此,函数需要加上extern "C"
前缀,这表示告诉编译器按C语言风格编译该函数。同时,为了避免编译器在编译优化阶段把没有使用到的符号删除掉,需要在函数前面加上__attribute__((visibility("default")))
和__attribute__((used))
。
C 语言函数的定义格式如下:
#include <stdint.h>extern "C" __attribute__((visibility("default"))) __attribute__((used))int32_t native_add(int32_t x, int32_t y) {return x + y;}
为了编写方便,可以使用宏定义来简化代码:
#include <stdint.h>#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))DART_API int32_t native_add(int32_t x, int32_t y) {return x + y;}
1.2 Dart函数类型定义
在 Dart 中,需要定义与 C 函数相对应的函数类型,以便在 Dart 中调用。
这里需要使用 Dart Function 定义两个函数类型,一个用于表示 C ,一个用于 Dart,如下:
///对应C语言函数声明:int32_t native_add(int32_t x, int32_t y)typedef Native_add = Int32 Function(Int32 x, Int32 y);///Dart使用的函数typedef FFI_add = int Function(int x, int y);
说明:
上面的Native_add
函数类型,是 C 函数在 Dart 中的表示形式,函数的参数和返回值都属性NativeType
(例如:Int32
);上面的FFI_add
函数类型,是 Dart 风格的函数,供 Dart 调用,函数参数和返回值都是 Dart 中的数据类型;当使用 FFI 绑定这两个函数类型的时候,数据类型的转换将由 FFI 内部完成。
1.3 Dart调用C函数
完成函数的声明和定义之后,就可以通过以下步骤在 Dart 中调用 C 函数了:
Android 平台使用DynamicLibrary.open()
加载符号信息,iOS 平台使用DynamicLibrary.process()
加载符号信息;通过DynamicLibrary.lookup()
或者DynamicLibrary.lookupFunction()
来查找函数符号,并转为 Dart 函数;
调用 Dart 函数。示例代码如下:
//加载符号DynamicLibrary nativeApi = Platform.isAndroid? DynamicLibrary.open("libnative_ffi.so"): DynamicLibrary.process();//方法1 - 查找函数符号,并转为Dart函数final addFunc1 = nativeApi.lookupFunction<Native_add, FFI_add>("native_add");//方法2 - 查找函数符号,并转为Dart函数FFI_add addFunc2 = nativeApi.lookup<NativeFunction<Native_add>>("native_add").asFunction();//此时,调用Dart函数就是调用C函数int result = addFunc1(1, addFunc2(1, 2));print("1+(1+2)=$result");//输出信息://1+(1+2)=4
说明:
lookupFunction()
其实就是lookup()
和asFunction()
的封装,简化代码。
1.4 变长参数函数
C 语言中具有变长参数函数,但是在 FFI 中,必须要指定参数类型和个数才行。
下面这个示例演示了如何在 Dart 调用 C 的变长参数函数:
首先,在 C 中定义一个函数:
#include <stdint.h>#include <stdarg.h>#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))DART_API int multi_sum(int nr_count, ...) {va_list nums;va_start(nums, nr_count);int sum = 0;for (int i = 0; i < nr_count; i++){sum += va_arg(nums, int);}va_end(nums);return sum;}
然后,在 Dart 中定义两个函数类型,与 C 中的函数进行映射:
///对应C语言函数typedef Native_multi_sum = Int32 Function(Int32 numCount, Int32 a, Int32 b, Int32 c);///供Dart所使用的函数typedef FFI_multi_sum = int Function(int numCount, int a, int b, int c);
最后,通过lookupFunction()
找到该符号并调用:
final sumFunc = nativeApi.lookupFunction<Native_multi_sum,FFI_multi_sum>("multi_sum");print("result:${sumFunc(3, 1, 2, 3)}");//输出结果//result:6
说明:
无
2、C 调用 Dart 函数
上面介绍了如何在 Dart 中调用 C 的函数,下面看看如何在 C 中调用 Dart 的函数。
在 Dart 中,只有全局函数才能给被 C 调用。我们可以通过Pointer.fromFunction()
函数将 Dart Function 转为 C 的函数指针。fromFunction()
函数声明如下:
/// Convert Dart function to a C function pointer, automatically marshalling/// the arguments and return value////// If an exception is thrown while calling `f()`, the native function will/// return `exceptionalReturn`, which must be assignable to return type of `f`.////// The returned function address can only be invoked on the mutator (main)/// thread of the current isolate. It will abort the process if invoked on any/// other thread.////// The pointer returned will remain alive for the duration of the current/// isolate's lifetime. After the isolate it was created in is terminated,/// invoking it from native code will cause undefined behavior.////// Does not accept dynamic invocations -- where the type of the receiver is/// [dynamic].external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(@DartRepresentationOf("T") Function f,[Object? exceptionalReturn]);
下面通过一个示例来演示 C 如何调用 Dart 函数。
首先,在 C 定义一个函数:
#include <malloc.h>#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))DART_APIvoid calc(int32_t src, void (*callback)(int32_t, int32_t)) {int result = src * 10000;callback(src, result);}
然后在 Dart 定义相应的函数类型以及 全局回调函数:
typedef Callback = Void Function(Int32, Int32);typedef Native_calc = Void Function(Int32, Pointer<NativeFunction<Callback>>);typedef FFI_calc = void Function(int, Pointer<NativeFunction<Callback>>);void globalCallback(int src, int result) {print("globalCallback src=$src, result=$result");}
说明:
函数指针的类型为:Pointer<NativeFunction<...>
;globalCallback()
是一个全局函数,用于给 C 调用。
最后,在 Dart 调用 C 的函数:
//加载 C 符号DynamicLibrary nativeApi = Platform.isAndroid? DynamicLibrary.open("libnative_ffi.so"): DynamicLibrary.process();//查找函数FFI_calc calcFunc = nativeApi.lookupFunction<Native_calc, FFI_calc>("calc");//调用函数calcFunc(32, Pointer.fromFunction(globalCallback));//输出结果://I/flutter ( 3920): globalCallback src=32, result=320000
说明:
我们在 Dart 中调用了 C 的calc()
函数,并将globalCallback
转为函数指针作为calc()
的参数;在 C 的calc()
函数内,直接把 Dart 的globalCallback
当作函数指针进行调用;
最后globalCallback
被调用,打印了相应的信息。
3、总结
上面介绍如何通过 FFI 实现 C 与 Dart 的相互调用,在后面的章节中,将会介绍字符串、结构体、数组、内存管理等知识,欢迎关注。
如果觉得《03 Flutter FFI 函数》对你有帮助,请点赞、收藏,并留下你的观点哦!