使用 dart:ffi 进行 C 互操作
在 Dart Native 平台 上运行的 Dart 移动应用、命令行应用和服务器应用可以使用 dart:ffi
库来调用本机 C API,以及读取、写入、分配和释放本机内存。FFI 代表 外部函数接口。 其他表示类似功能的术语包括本机接口和语言绑定。
API 文档可在 dart:ffi
API 参考 中找到。
下载示例文件
#要使用本指南中的示例,请下载完整的 ffi 示例 目录。它包含以下示例,展示了如何使用 dart:ffi
库
示例 | 描述 |
---|---|
hello_world | 如何调用没有参数且没有返回值的 C 函数。 |
primitives | 如何调用具有参数和返回值的 C 函数,这些参数和返回值为整数或指针。 |
structs | 如何使用结构体将字符串传递到 C 和从 C 传递回来,以及如何处理简单和复杂的 C 结构体。 |
test_utils | 所有这些示例的通用测试实用程序。 |
查看 hello_world 示例
#在 hello_world 示例 中包含了调用 C 库所需的最小代码。此示例可以在您在上一个部分中下载的 samples/ffi
中找到。
文件
#hello_world
示例包含以下文件
源文件 | 描述 |
---|---|
hello.dart | 一个 Dart 文件,它使用来自 C 库的 hello_world() 函数。 |
pubspec.yaml | Dart pubspec 文件,SDK 下限为 3.4。 |
hello_library/hello.h | 声明 hello_world() 函数。 |
hello_library/hello.c | 一个 C 文件,它导入 hello.h 并定义 hello_world() 函数。 |
hello_library/hello.def | 一个模块定义文件,它指定构建 DLL 时使用信息。 |
hello_library/CMakeLists.txt | 一个用于将 C 代码编译成动态库的 CMake 构建文件。 |
构建 C 库会创建多个文件,包括一个名为 libhello.dylib
(macOS)、libhello.dll
(Windows)或 libhello.so
(Linux)的动态库文件。
构建和执行
#构建动态库和执行 Dart 应用的命令类似于以下系列。
$ cd hello_library
$ cmake .
...
$ make
...
$ cd ..
$ dart pub get
$ dart run hello.dart
Hello World
利用 dart:ffi
#要了解如何使用 dart:ffi
库调用 C 函数,请查看 hello.dart
文件。本节解释了此文件的内容。
导入
dart:ffi
。dartimport 'dart:ffi' as ffi;
导入您将用于存储动态库路径的 path 库。
dartimport 'dart:io' show Platform, Directory; import 'package:path/path.dart' as path;
使用 C 函数的 FFI 类型签名创建类型定义。
要了解根据dart:ffi
库最常用的类型,请参阅 与本机类型交互。darttypedef hello_world_func = ffi.Void Function();
创建用于调用 C 函数时使用的变量的
typedef
。darttypedef HelloWorld = void Function();
创建一个变量来存储动态库的路径。
dartvar libraryPath = path.join(Directory.current.path, 'hello_library', 'libhello.so'); if (Platform.isMacOS) { libraryPath = path.join(Directory.current.path, 'hello_library', 'libhello.dylib'); } else if (Platform.isWindows) { libraryPath = path.join(Directory.current.path, 'hello_library', 'Debug', 'hello.dll'); }
打开包含 C 函数的动态库。
dartfinal dylib = ffi.DynamicLibrary.open(libraryPath);
获取对 C 函数的引用,并将其放入变量中。此代码使用步骤 2 和 3 中的
typedefs
,以及步骤 4 中的动态库变量。dartfinal HelloWorld hello = dylib .lookup<ffi.NativeFunction<hello_world_func>>('hello_world') .asFunction();
调用 C 函数。
darthello();
了解 hello_world
示例后,请参阅 其他 dart:ffi
示例。
捆绑和加载 C 库
#捆绑/打包/分发然后加载本机 C 库的方法取决于平台和库类型。
要了解如何操作,请参阅以下页面和示例。
- Flutter
dart:ffi
用于 Android 应用 - Flutter
dart:ffi
用于 iOS 应用 - Flutter
dart:ffi
用于 macOS 应用 dart:ffi
示例
与本机类型交互
#dart:ffi
库提供了多种类型,这些类型实现了 NativeType
并表示 C 中的本机类型。您可以实例化某些本机类型。其他一些本机类型只能用作类型签名中的标记。
可以实例化这些类型签名标记
#以下本机类型可用作类型签名中的标记。它们或其子类型可以在 Dart 代码中实例化。
仅用作类型签名标记
#以下列表显示了哪些平台无关的本机类型用作类型签名中的标记。它们不能在 Dart 代码中实例化。
Dart 类型 | 描述 |
---|---|
布尔值 | 表示 C 中的本机布尔值。 |
双精度浮点数 | 表示 C 中的本机 64 位双精度浮点数。 |
单精度浮点数 | 表示 C 中的本机 32 位单精度浮点数。 |
8 位有符号整数 | 表示 C 中的本机 8 位有符号整数。 |
16 位有符号整数 | 表示 C 中的本机 16 位有符号整数。 |
32 位有符号整数 | 表示 C 中的本机 32 位有符号整数。 |
64 位有符号整数 | 表示 C 中的本机 64 位有符号整数。 |
本机函数 | 表示 C 中的函数类型。 |
不透明类型 | C 中所有不透明类型的超类型。 |
8 位无符号整数 | 表示 C 中的本机 8 位无符号整数。 |
16 位无符号整数 | 表示 C 中的本机 16 位无符号整数。 |
32 位无符号整数 | 表示 C 中的本机 32 位无符号整数。 |
64 位无符号整数 | 表示 C 中的本机 64 位无符号整数。 |
空类型 | 表示 C 中的 void 类型。 |
还有许多 ABI 特定的标记本机类型扩展了 AbiSpecificInteger。要了解这些类型如何在特定平台上映射,请参阅以下表格中链接的 API 文档。
Dart 类型 | 描述 |
---|---|
AbiSpecificInteger | 所有 ABI 特定整数类型的超类型。 |
整数 | 表示 C 中的 int 类型。 |
整数指针 | 表示 C 中的 intptr_t 类型。 |
长整数 | 表示 C 中的 long int (long )类型。 |
长长整数 | 表示 C 中的 long long 类型。 |
短整数 | 表示 C 中的 short 类型。 |
有符号字符 | 表示 C 中的 signed char 类型。 |
大小 | 表示 C 中的 size_t 类型。 |
无符号整数指针 | 表示 C 中的 uintptr_t 类型。 |
无符号字符 | 表示 C 语言中的 unsigned char 类型。 |
无符号整数 | 表示 C 语言中的 unsigned int 类型。 |
无符号长整数 | 表示 C 语言中的 unsigned long int 类型。 |
无符号长长整数 | 表示 C 语言中的 unsigned long long 类型。 |
无符号短整数 | 表示 C 语言中的 unsigned short 类型。 |
宽字符 | 表示 C 语言中的 wchar_t 类型。 |
使用 package:ffigen
生成 FFI 绑定
#对于大型 API 表面,编写与 C 代码集成的 Dart 绑定可能非常耗时。要让 Dart 从 C 头文件创建 FFI 包装器,请使用 package:ffigen
绑定生成器。
构建和捆绑本地资源
#“原生资源”功能应解决与依赖原生代码的 Dart 包分发相关的一些问题。它通过提供统一的挂钩来与构建 Flutter 和独立 Dart 应用程序所涉及的各种构建系统集成来实现。
此功能应简化 Dart 包依赖和使用原生代码的方式。“原生资源”应提供以下好处
- 使用包的
hook/build.dart
构建挂钩构建原生代码或获取二进制文件。 - 捆绑
build.dart
构建挂钩报告的原生Asset
。 - 通过使用
assetId
的声明式@Native<>() extern
函数在运行时提供原生资源。
当您选择加入原生实验时,flutter (run|build)
和 dart (run|build)
命令将与 Dart 代码一起构建和捆绑原生代码。
查看 native_add_library
示例
#native_add_library
示例包含构建和捆绑 Dart 包中 C 代码的最小代码。
该示例包含以下文件
源文件 | 描述 |
---|---|
src/native_add_library.c | 包含 add 代码的 C 文件。 |
lib/native_add_library.dart | 通过 FFI 调用资源 package:native_add_library/native_add_library.dart 中的 C 函数 add 的 Dart 文件。(请注意,资源 ID 默认设置为库 URI。) |
test/native_add_library_test.dart | 使用原生代码的 Dart 测试。 |
hook/build.dart | 用于编译 src/native_add_library.c 并声明具有 ID package:native_add_library/native_add_library.dart 的已编译资源的构建挂钩。 |
当 Dart 或 Flutter 项目依赖于 package:native_add_library
时,它会在 run
、build
和 test
命令上调用 hook/build.dart
构建挂钩。native_add_app
示例展示了 native_add_library
的用法。
查看本地资源 API 文档
#以下包提供了 API 文档
- 要了解 Dart FFI 中的原生资源,请查阅
Native
和DefaultAsset
的dart:ffi
API 参考。 - 要了解
hook/build.dart
构建挂钩,请查阅package:native_assets_cli
API 参考。
选择加入实验
#要了解如何启用实验并提供反馈,请查阅以下跟踪问题
除非另有说明,否则本网站上的文档反映了 Dart 3.5.3。页面最后更新于 2024-05-30。 查看源代码 或 报告问题.