使用 dart:ffi 绑定到原生 Android 代码
Flutter 移动和桌面应用可以使用 dart:ffi 库调用原生 C API。FFI 是 外部函数接口 的缩写。其他类似的术语包括原生接口和语言绑定。
在您的库或程序可以使用 FFI 库绑定到原生代码之前,您必须确保原生代码已加载且其符号对 Dart 可见。本页重点介绍在 Flutter 插件或应用中编译、打包和加载 Android 原生代码。
本教程演示了如何在 Flutter 插件中捆绑 C/C++ 源文件,并使用 Dart FFI 库在 Android 和 iOS 上将它们绑定起来。在此过程中,您将创建一个 C 函数来实现 32 位加法,然后通过一个名为“native_add”的 Dart 插件将其公开。
动态链接与静态链接
#原生库可以动态或静态地链接到应用中。静态链接库嵌入到应用的 executables 映像中,并在应用启动时加载。
可以通过 DynamicLibrary.executable
或 DynamicLibrary.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 生成的。
其他用例
#平台库
#要链接到平台库,请使用以下说明
在 Android 文档的 Android NDK 原生 API 列表中查找所需的库。这将列出稳定的原生 API。
使用
DynamicLibrary.open
加载库。例如,要加载 OpenGL ES(v3):dartDynamicLibrary.open('libGLES_v3.so');
如果文档有指示,您可能需要更新应用或插件的 Android manifest 文件。
第一方库
#无论是一个应用还是一个插件,将原生代码包含在源代码或二进制形式中的过程是相同的。
开源第三方
#请按照 Android 文档中的 将 C 和 C++ 代码添加到您的项目 说明来添加原生代码和对原生代码工具链(CMake 或 ndk-build
)的支持。
闭源第三方库
#要创建一个包含 Dart 源代码的 Flutter 插件,但以二进制形式分发 C/C++ 库,请使用以下说明:
- 打开您项目中的
android/build.gradle
文件。 - 将 AAR 库添加为依赖项。不要将该库包含在您的 Flutter 包中。而是应该从仓库(如 JCenter)下载。
Android APK 大小(共享对象压缩)
#一般的 Android 指南 建议以未压缩的形式分发原生共享对象,因为这实际上可以节省设备空间。共享对象可以直接从 APK 加载,而无需在设备上将其解压到临时位置然后再加载。APK 在传输过程中已经过压缩——这就是为什么您应该关注下载大小。
Flutter APK 默认不遵循这些指南,并且会压缩 libflutter.so
和 libapp.so
——这导致 APK 尺寸更小,但设备上的尺寸更大。
第三方共享对象可以通过其 AndroidManifest.xml
中的 android:extractNativeLibs="true"
来更改此默认设置,从而停止压缩 libflutter.so
、libapp.so
以及任何用户添加的共享对象。要重新启用压缩,请在 your_app_name/android/app/src/main/AndroidManifest.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 互操作性的信息,请查看以下视频