FFi
MacOS 12.0
CMake:: cmake version 3.21.2
Make: GNU Make 3.81
Dart: Dart SDK version: 2.15.0÷÷
FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi
库来调用 C 语言编写的代码。
在 Flutter 2.0 中的 Dart 2.12 已发布,其中包含健全的空安全和 Dart FFI 的稳定版, 并且提供了一套类型绑定生成工具 ffigen,可以自动生成 Dart Wrapper 加快开发效率。
如何将用 C/C++ 编写的代码带入到 Flutter 应用中
关于Dart语言FFI使用 ,大概步骤
- 导入ffi包
- 为被调用的c函数创建Dart Native签名
- 为被调用c函数创建Dart函数
- 加载动态库
- 查找c函数,并将c函数指针映射dart函数对象
- 调用函数
解决的问题
- 可以同步调用C API,不像Flutter Channel一开始就是异步
- 调用C语言更快,不像之前需要通过Native中转(或者改Flutter引擎代码)
方式
Flutter->原生JNI->C
Flutter->FFI->C
原始FFI
将已有的 c/cpp
通过交叉编译各个平台,windows
的dll
,unix
系统的so
库,然后通过DynamicLibrary
的open
函数去打开这个库,来获得其中函数的动态链接。随即按套路编写dart
调用ffi
的代码即可。
ffgen
每个平台都有一些已经有的so
库,如果我们的 c/cpp 语言实现的都是一些所有unix
设备或者windows
设备底层自带so、dll
中的函数,我们不妨将里面每一个函数都转换成dart
端可以对应调用的函数,也就是生成一个dart
可用的 ".h" 头,当然这个扩展名还是 ".dart" 。
这样一来,我们无需再为了 windows、macos、linux、android、ios
这5个平台分别交叉编译一份动态库,我们只需要写一份通用的dart
代码即可。
Mac电脑确保安装llvm
brew install --force-bottle llvm@3.9
/usr/local/opt/llvm # 确保可以找到
添加依赖pubspec.yaml
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# FFI
ffi: ^1.1.2
ffigen: ^4.1.2
添加c文件 ios/Runner/native.cpp
ios/Runner/native.h
ffigen:
name: NativeLibrary
description: Bindings to `library/hello.h`.
output: 'lib/c.dart'
headers:
entry-points:
- 'ios/Runner/native.h'
使用ffigen生成dart代码 - 终端执行
➜ ffi dart run ffigen
Dart Use
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:ffi/ffi.dart';
常用属性与方法介绍
Dart与C语言,Dart FFI
提供了很多方法
import 'package:ffi'
DynamicLibrary.open
它可以加载动态链接库
external factory DynamicLibrary.open(String path);
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open("libnative_add.so");
DynamicLibrary.process
import 'dart:io' show Platform;
import 'dart:ffi' as ffi;
late ffi.DynamicLibrary native;
native = Platform.isAndroid
? ffi.DynamicLibrary.open("libnative_add.so")
: ffi.DynamicLibrary.process();
它可以用于在iOS及MacOS中加载应用程序已经自动加载好的动态链接库,也可以解析静态链接到应用的二进制文件符号。需要注意的是,它不能用于windows平台
DynamicLibrary.executable
external factory DynamicLibrary.executable();
它可用于加载静态链接库
NativeType
NativeType
是在Dart中表示C语言中的数据结构
Pointer
它是C语言中指针在Dart中的映射
DynamicLibrary->lookup()
external Pointer<T> lookup<T extends NativeType>(String symbolName);
它用于在DynamicLibrary中查找到对应的符号并返回其内存地址。
final dylib = DynamicLibrary.open(libraryPath);
late final _hello_worldPtr =
dylib.lookup<NativeFunction<Void Function()>>('hello_world');
late final _hello_world = _hello_worldPtr.asFunction<void Function()>();
_hello_world();
Pointer.fromAddress(int ptr)
根据内存地址获取C对象指针
// 创建一个指向NULL的Native指针
final Pointer<Never> nullptr = Pointer.fromAddress(0);
Pointer.fromFunction
根据一个Dart函数,创建一个Native函数指针,一般用于将Dart函数传给C,使C有调用Dart函数的能力
void globalCallback(int src, int result) {
print("globalCallback src=$src, result=$result");
}
Pointer.fromFunction(globalCallback);
Pointer->address()
获取指针的内存地址
asFunction
将Native指针对象,转换为Dart函数
sizeOf
返回具体类型的内存占用
例
ffi.sizeOf<ffi.Int64>(); // 8
复制代码
malloc.allocate()
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
复制代码
开辟一块大小byteCount
的空间
例
Pointer<Uint8> bytes = malloc.allocate<Uint8>(ffi.sizeOf<ffi.Uint8>());
复制代码
malloc.free
释放内存
malloc.free(bytes);
Dart FFI与C基础数据类型映射表
Dart 中定义的NativeType | C语言中的类型 | 说明 |
---|---|---|
Opaque | opaque | 不暴露其成员类型,一般用于表示C++中的类 |
Int8 | int8_t 或 char | 有符号8位整数 |
Int16 | int16_t 或 short | 有符号16位整数 |
Int32 | int32_t 或 int | 有符号32位整数 |
Int64 | int64_t 或 long long | 有符号64位整数 |
Uint8 | uint8_t 或 unsigned char | 无符号8位整数 |
Uint16 | uint16_t 或 unsigned short | 无符号16位整数 |
Uint32 | int32_t 或 unsigned int | 无符号32位整数 |
Uint64 | uint64_t 或 unsigned long long | 无符号64位整数 |
IntPtr | int* | 整数类型指针 |
Float | float | 单精度浮点类型 |
Double | double | 双精度浮点类型 |
Void | void | void类型 |
Handle | Dart_Handle Dart | 句柄在C中的表示形式 |
NativeFunction | 函数 | 函数类型 |
Struct | struct | 结构体类型 |
Union | union | 共同体类型 |
Pointer | * | 指针类型 |
nullptr | NULL | 空指针 |
dynamic | Dart_CObject | Dart对象在C中的表现 |
Dart Call C
如何找到 C++ 中的库
通过 DynamicLibrary.open()
dlopen 加载动态链接库,并返回句柄。
ffigen
https://link.juejin.cn/?target=https%3A%2F%2Fpub.dev%2Fpackages%2Fffigen库来根据C/C++头文件自动生成dart 函数
dart run ffigen
注意
ffigen
只能自动生成C风格的头文件,如果你的头文件中包含了C++风格代码如class,需要使用#ifdef __cplusplus #endif包裹起来
/// 两个数相加
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
参考
用到的工具库
- ffi
- ffigen: https://pub.dev/packages/ffigen
文档
https://dart.cn/guides/libraries/c-interop
https://api.dart.cn/stable/2.15.1/dart-ffi/dart-ffi-library.html
FFi
MacOS 12.0
CMake:: cmake version 3.21.2
Make: GNU Make 3.81
Dart: Dart SDK version: 2.15.0÷÷
FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi
库来调用 C 语言编写的代码。
在 Flutter 2.0 中的 Dart 2.12 已发布,其中包含健全的空安全和 Dart FFI 的稳定版, 并且提供了一套类型绑定生成工具 ffigen,可以自动生成 Dart Wrapper 加快开发效率。
如何将用 C/C++ 编写的代码带入到 Flutter 应用中
关于Dart语言FFI使用 ,大概步骤
- 导入ffi包
- 为被调用的c函数创建Dart Native签名
- 为被调用c函数创建Dart函数
- 加载动态库
- 查找c函数,并将c函数指针映射dart函数对象
- 调用函数
解决的问题
- 可以同步调用C API,不像Flutter Channel一开始就是异步
- 调用C语言更快,不像之前需要通过Native中转(或者改Flutter引擎代码)
方式
Flutter->原生JNI->C
Flutter->FFI->C
原始FFI
将已有的 c/cpp
通过交叉编译各个平台,windows
的dll
,unix
系统的so
库,然后通过DynamicLibrary
的open
函数去打开这个库,来获得其中函数的动态链接。随即按套路编写dart
调用ffi
的代码即可。
ffgen
每个平台都有一些已经有的so
库,如果我们的 c/cpp 语言实现的都是一些所有unix
设备或者windows
设备底层自带so、dll
中的函数,我们不妨将里面每一个函数都转换成dart
端可以对应调用的函数,也就是生成一个dart
可用的 ".h" 头,当然这个扩展名还是 ".dart" 。
这样一来,我们无需再为了 windows、macos、linux、android、ios
这5个平台分别交叉编译一份动态库,我们只需要写一份通用的dart
代码即可。
Mac电脑确保安装llvm
brew install --force-bottle llvm@3.9
/usr/local/opt/llvm # 确保可以找到
添加依赖pubspec.yaml
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# FFI
ffi: ^1.1.2
ffigen: ^4.1.2
添加c文件 ios/Runner/native.cpp
ios/Runner/native.h
ffigen:
name: NativeLibrary
description: Bindings to `library/hello.h`.
output: 'lib/c.dart'
headers:
entry-points:
- 'ios/Runner/native.h'
使用ffigen生成dart代码 - 终端执行
➜ ffi dart run ffigen
Dart Use
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:ffi/ffi.dart';
常用属性与方法介绍
Dart与C语言,Dart FFI
提供了很多方法
import 'package:ffi'
DynamicLibrary.open
它可以加载动态链接库
external factory DynamicLibrary.open(String path);
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open("libnative_add.so");
DynamicLibrary.process
import 'dart:io' show Platform;
import 'dart:ffi' as ffi;
late ffi.DynamicLibrary native;
native = Platform.isAndroid
? ffi.DynamicLibrary.open("libnative_add.so")
: ffi.DynamicLibrary.process();
它可以用于在iOS及MacOS中加载应用程序已经自动加载好的动态链接库,也可以解析静态链接到应用的二进制文件符号。需要注意的是,它不能用于windows平台
DynamicLibrary.executable
external factory DynamicLibrary.executable();
它可用于加载静态链接库
NativeType
NativeType
是在Dart中表示C语言中的数据结构
Pointer
它是C语言中指针在Dart中的映射
DynamicLibrary->lookup()
external Pointer<T> lookup<T extends NativeType>(String symbolName);
它用于在DynamicLibrary中查找到对应的符号并返回其内存地址。
final dylib = DynamicLibrary.open(libraryPath);
late final _hello_worldPtr =
dylib.lookup<NativeFunction<Void Function()>>('hello_world');
late final _hello_world = _hello_worldPtr.asFunction<void Function()>();
_hello_world();
Pointer.fromAddress(int ptr)
根据内存地址获取C对象指针
// 创建一个指向NULL的Native指针
final Pointer<Never> nullptr = Pointer.fromAddress(0);
Pointer.fromFunction
根据一个Dart函数,创建一个Native函数指针,一般用于将Dart函数传给C,使C有调用Dart函数的能力
void globalCallback(int src, int result) {
print("globalCallback src=$src, result=$result");
}
Pointer.fromFunction(globalCallback);
Pointer->address()
获取指针的内存地址
asFunction
将Native指针对象,转换为Dart函数
sizeOf
返回具体类型的内存占用
例
ffi.sizeOf<ffi.Int64>(); // 8
复制代码
malloc.allocate()
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
复制代码
开辟一块大小byteCount
的空间
例
Pointer<Uint8> bytes = malloc.allocate<Uint8>(ffi.sizeOf<ffi.Uint8>());
复制代码
malloc.free
释放内存
malloc.free(bytes);
Dart FFI与C基础数据类型映射表
Dart 中定义的NativeType | C语言中的类型 | 说明 |
---|---|---|
Opaque | opaque | 不暴露其成员类型,一般用于表示C++中的类 |
Int8 | int8_t 或 char | 有符号8位整数 |
Int16 | int16_t 或 short | 有符号16位整数 |
Int32 | int32_t 或 int | 有符号32位整数 |
Int64 | int64_t 或 long long | 有符号64位整数 |
Uint8 | uint8_t 或 unsigned char | 无符号8位整数 |
Uint16 | uint16_t 或 unsigned short | 无符号16位整数 |
Uint32 | int32_t 或 unsigned int | 无符号32位整数 |
Uint64 | uint64_t 或 unsigned long long | 无符号64位整数 |
IntPtr | int* | 整数类型指针 |
Float | float | 单精度浮点类型 |
Double | double | 双精度浮点类型 |
Void | void | void类型 |
Handle | Dart_Handle Dart | 句柄在C中的表示形式 |
NativeFunction | 函数 | 函数类型 |
Struct | struct | 结构体类型 |
Union | union | 共同体类型 |
Pointer | * | 指针类型 |
nullptr | NULL | 空指针 |
dynamic | Dart_CObject | Dart对象在C中的表现 |
Dart Call C
如何找到 C++ 中的库
通过 DynamicLibrary.open()
dlopen 加载动态链接库,并返回句柄。
ffigen
https://link.juejin.cn/?target=https%3A%2F%2Fpub.dev%2Fpackages%2Fffigen库来根据C/C++头文件自动生成dart 函数
dart run ffigen
注意
ffigen
只能自动生成C风格的头文件,如果你的头文件中包含了C++风格代码如class,需要使用#ifdef __cplusplus #endif包裹起来
/// 两个数相加
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
参考
用到的工具库
- ffi
- ffigen: https://pub.dev/packages/ffigen
文档
https://dart.cn/guides/libraries/c-interop
https://api.dart.cn/stable/2.15.1/dart-ffi/dart-ffi-library.html