在 macOS 中使用 dart:ffi 调用本地代码

Flutter 移动版可以使用 dart:ffi 库来调用本地的 C API。 FFI 代表 外部功能接口。类似功能的其他术语包括 本地接口语言绑定

你必须首先确保本地代码已加载,并且其符号对 Dart 可见,然后才能在库或程序使用 FFI 库绑定本地代码。本页主要介绍如何在 Flutter 插件或应用程序中编译、打包和加载 macOS 本地代码。

本教程演示了如何在 Flutter 插件中捆绑 C/C++ 源代码,并使用 macOS 上的 Dart FFI 库绑定它们。在本示例中,你将创建一个实现 32 位的加法 C 函数,然后通过名为 “native_add” 的 Dart 插件暴露它。

动态链接 vs 静态链接

本地库可以动态或静态地链接到应用程序中。一个静态链接库会被嵌入到应用程序的可执行映像中,并在应用程序启动时加载。

静态链接中的符号可以使用 DynamicLibrary.executableDynamicLibrary.process 来加载。

相比之下,动态链接库则分布在应用程序中的单独的文件或文件夹中,并按需加载。在 macOS 上,它是作为 .framework 文件夹分发的。

动态链接库在 Dart 中可以通过 DynamicLibrary.open 加载。

Dart dev 频道中的 API 已经可用: Dart API 参考文档

创建 FFI 插件

如果你已经有一个插件,跳过这步。

如果要创建一个名为 “native_add” 的插件,你需要这么做:

$ flutter create --platforms=macos --template=plugin_ffi native_add
$ cd native_add

C/C++ 源代码会被创建至 native_add/src。这些源代码在不同平台构建时会生成对应平台的构建文件夹。

FFI 库只能绑定 C 语言的符号,所以 C++ 语言的符号会被标记为 extern "C"

你还应该添加属性来表明符号是需要被 Dart 引用的,以防止链接器在优化链接时会丢弃符号。 __attribute__((visibility("default"))) __attribute__((used)).

在 iOS 上,native_add/macos/native_add.podspec 负责关联这些代码。

原生代码会从 lib/native_add_bindings_generated.dart 中被 Dart 调用。

绑定代码由 package:ffigen 生成。

其他的用例

iOS 和 macOS

动态链接库在应用程序启动时由动态链接器自动加载。它们的组成符号可以用 DynamicLibrary.process。你还可以使用 DynamicLibrary.open 来限制符号解析的范围,但目前仍然不确定苹果的审查程序将如何处理两者的使用。

你可以使用 DynamicLibrary.executableDynamicLibrary.process 解析静态链接到应用程序二进制文件的符号。

平台库

要链接到平台库,请按照如下说明:

  1. 在 Xcode 中,打开 Runner.xcworkspace

  2. 选择目标设备。

  3. Linked Frameworks and Libraries 中点击 +

  4. 选择要链接的系统库。

第一方库

第一方本地库可以作为源文件或(已签名的).framework 文件被包含在内。它也可能包括静态链接的档案,但需要测试。

源码

要直接链接到源代码,请按照如下说明:

  1. 在 Xcode 中,打开 Runner.xcworkspace

  2. 添加 C/C++/Objective-C/Swift 源码到 Xcode 工程中。

  3. 将以下前缀添加到导出的符号声明中,以确保它们对 Dart 可见:

    C/C++/Objective-C

    extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used))
    

    Swift

    @_cdecl("myFunctionName")
    

已编译的动态库

要链接到已编译过的动态库,请按照如下说明:

  1. 如果存在已进行签名的 Framework 文件,请打开 Runner.xcworkspace

  2. 添加 framework 文件到 Embedded Binaries 区域中。

  3. 同时将其添加到 Xcode 中目标的 Linked Frameworks & Libraries 部分。

已编译的(动态)库 (macOS)

要添加一个闭源的库到 Flutter macOS 桌面 应用,请按照如下说明:

  1. 按照 Flutter 桌面的使用说明来创建 Flutter 桌面应用程序。

  2. 在 Xcode 中打开 yourapp/macos/Runner.xcworkspace

    1. 拖动你已经预编译的 libyourlibrary.dylib 到你的 Runner/Frameworks

    2. 点击 Runner 然后进入 Build Phases 标签。

      1. 拖动 libyourlibrary.dylibCopy Bundle Resources 列表。

      2. Embed Libararies 下,检查 Code Sign on Copy

      3. Link Binary With Libraries 下,设置状态为 Optional。(我们使用动态链接,不需要静态链接)

    3. 点击 Runner 然后进入 General 标签页。

      1. 拖动 libyourlibrary.dylibFrameworks, Libararies and Embedded Content 列表中。

      2. 选择 Embed & Sign

    4. 点击 Runner 然后进入 Build Settings 标签页。

      1. Search Paths 部分,配置 Library Search Paths 确保 libyourlibrary.dylib 的路径包括在内。

  3. 编辑 lib/main.dart 文件。

    1. 使用 DynamicLibrary.open('libyourlibrary.dylib') 来动态链接符号表。

    2. 在 widget 的某个地方调用你的本地代码。

  4. 运行 flutter run 然后检查你的本地方法的调用结果。

  5. 运行 flutter build macos 去构建一个自包含的 release 版本的应用。

Other Resources

To learn more about C interoperability, check out these videos: