跳到主內容

使用 dart:ffi 繫結到原生 Android 程式碼

要在 Flutter 程式中使用 C 程式碼,請使用 dart:ffi 庫。

Flutter 移動和桌面應用可以使用 dart:ffi 庫呼叫原生 C API。FFI外部函式介面 的縮寫。其他類似的術語包括原生介面語言繫結

在您的庫或程式可以使用 FFI 庫繫結到原生程式碼之前,您必須確保已載入原生程式碼,並且其符號對 Dart 可見。此頁面重點介紹在 Flutter 外掛或應用程式中編譯、打包和載入 Android 原生程式碼。

本教程演示瞭如何在 Flutter 外掛中捆綁 C/C++ 原始碼,並使用 Dart FFI 庫在 Android 和 iOS 上繫結到它們。在本演練中,您將建立一個實現 32 位加法的 C 函式,然後透過名為“native_add”的 Dart 外掛將其暴露出來。

動態連結與靜態連結

#

原生庫可以動態或靜態地連結到應用中。靜態連結庫嵌入到應用的 executables 映像中,並在應用啟動時載入。

可以使用 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 Native APIs 列表中找到所需的庫。此列表列出了穩定的原生 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 載入,而無需將其解壓到裝置上的臨時位置再進行載入。APK 在傳輸過程中也會被打包——因此您應該檢視下載大小。

預設情況下,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 互操作性的資訊,請檢視以下影片