跳至主要內容

使用 dart:ffi 綁定原生 Android 程式碼

Flutter 行動和桌面應用程式可以使用 dart:ffi 函式庫來呼叫原生 C API。FFI 代表 外部函式介面。其他類似功能的術語包括原生介面語言綁定

在您的函式庫或程式可以使用 FFI 函式庫綁定原生程式碼之前,您必須確保原生程式碼已載入,且其符號對 Dart 可見。本頁重點介紹如何在 Flutter 外掛程式或應用程式中編譯、封裝和載入 Android 原生程式碼。

本教學示範如何將 C/C++ 原始碼捆綁到 Flutter 外掛程式中,並在 Android 和 iOS 上使用 Dart FFI 函式庫將其綁定。在本演練中,您將建立一個實作 32 位元加法的 C 函式,然後透過名為「native_add」的 Dart 外掛程式公開它。

動態與靜態連結

#

原生函式庫可以動態或靜態連結到應用程式中。靜態連結的函式庫會嵌入到應用程式的可執行映像檔中,並在應用程式啟動時載入。

可以使用 DynamicLibrary.executableDynamicLibrary.process 載入靜態連結函式庫中的符號。

相比之下,動態連結函式庫會以應用程式中的單獨檔案或資料夾形式發佈,並按需載入。在 Android 上,動態連結函式庫會以一組 .so (ELF) 檔案形式發佈,每個架構一個。

可以透過 DynamicLibrary.open 將動態連結函式庫載入 Dart 中。

API 文件可從Dart API 參考文件取得。

在 Android 上,僅支援動態函式庫 (因為主要可執行檔是 JVM,我們不會將其靜態連結)。

建立 FFI 外掛程式

#

若要建立名為「native_add」的 FFI 外掛程式,請執行下列步驟

flutter create --platforms=android,ios,macos,windows,linux --template=plugin_ffi native_add
cd native_add

這將建立一個在外掛程式中 native_add/src 包含 C/C++ 原始碼的外掛程式。這些原始碼由各種作業系統建置資料夾中的原生建置檔案建置。

FFI 函式庫只能針對 C 符號進行綁定,因此在 C++ 中,這些符號會標記為 extern "C"

您也應該新增屬性來指示這些符號是從 Dart 參照,以防止連結器在連結時最佳化期間捨棄這些符號。__attribute__((visibility("default"))) __attribute__((used))

在 Android 上,native_add/android/build.gradle 會連結程式碼。

原生程式碼會從 lib/native_add_bindings_generated.dart 中的 dart 呼叫。

這些綁定是使用 package:ffigen 產生的。

其他使用案例

#

平台程式庫

#

若要連結平台函式庫,請使用下列指示

  1. 在 Android 文件中的Android NDK 原生 API清單中找到所需的函式庫。此清單列出了穩定的原生 API。

  2. 使用 DynamicLibrary.open 載入函式庫。例如,若要載入 OpenGL ES (v3)

    dart
    DynamicLibrary.open('libGLES_v3.so');

如果文件指示,您可能需要更新應用程式或外掛程式的 Android 清單檔案。

第一方函式庫

#

在原始碼或二進位格式中包含原生程式碼的程序對於應用程式或外掛程式都是相同的。

開源協力廠商

#

遵循 Android 文件中的將 C 和 C++ 程式碼新增至您的專案指示,以新增原生程式碼並支援原生程式碼工具鏈 (CMake 或 ndk-build)。

封閉原始碼協力廠商函式庫

#

若要建立一個包含 Dart 原始碼的 Flutter 外掛程式,但以二進位格式發佈 C/C++ 函式庫,請使用下列指示

  1. 開啟專案的 android/build.gradle 檔案。
  2. 將 AAR 成品新增為相依性。不要在您的 Flutter 套件中包含該成品。相反地,它應該從儲存庫 (例如 JCenter) 下載。

Android APK 大小 (共享物件壓縮)

#

Android 指導方針通常建議不要壓縮原生共享物件,因為這樣實際上可以節省裝置空間。共享物件可以直接從 APK 載入,而不是在裝置上解壓縮到暫存位置然後再載入。APKs 在傳輸過程中也會被壓縮,這就是為什麼您應該關注下載大小。

Flutter APK 預設不遵循這些指導方針,並會壓縮 libflutter.solibapp.so,這會導致 APK 大小較小,但裝置上的大小較大。

協力廠商的共享物件可以使用其 AndroidManifest.xml 中的 android:extractNativeLibs="true" 變更此預設設定,並停止壓縮 libflutter.solibapp.so 以及任何使用者新增的共享物件。若要重新啟用壓縮,請使用下列方式覆寫 your_app_name/android/app/src/main/AndroidManifest.xml 中的設定。

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.your_app_name">
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.your_app_name" >
    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->

    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="your_app_name"
        android:icon="@mipmap/ic_launcher">
        android:icon="@mipmap/ic_launcher"
        android:extractNativeLibs="true"
        tools:replace="android:extractNativeLibs">

其他資源

#

若要進一步了解 C 互通性,請查看這些影片