在 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: