Android 上使用 dart:ffi 调用本地代码
Flutter 移动版可以使用 dart:ffi 库来调用本地的 C API。 FFI 代表 外部功能接口。类似功能的其他术语包括本地接口和语言绑定。
你必须首先确保本地代码已加载,并且其符号对 Dart 可见,然后才能在库或程序使用 FFI 库绑定本地代码。本页主要介绍如何在 Flutter 插件或应用程序中编译、打包和加载本地代码。
本教程演示了如何在 Flutter 插件中捆绑 C/C++ 源代码,并使用 Android 和 iOS 上的 Dart FFI 库绑定它们。在本示例中,你将创建一个实现 32 位的加法 C 函数,然后通过名为 "native_add" 的 Dart 插件暴露它。
动态链接 vs 静态链接
#本地库可以动态或静态地链接到应用程序中。一个静态链接库会被嵌入到应用程序的可执行映像中,并在应用程序启动时加载。
静态链接中的符号可以使用 DynamicLibrary.executable
或 DynamicLibrary.process
来加载。
相比之下,动态链接库则分布在应用程序中的单独的文件或文件夹中,并按需加载。在 Android 上,动态链接库作为一组
.so
(ELF 可执行与可链接格式)文件分发,每个架构各有一个。
动态链接库在 Dart 中可以通过 DynamicLibrary.open
加载。
Dart dev 频道中的 API 已经可用: Dart API 参考文档.
在 Android 平台只有动态库可以使用(因为在 JVM 环境无法静态链接)。
创建 FFI 插件
#如果要创建一个名为 "native_add" 的插件,你需要这么做:
flutter create --platforms=android,ios,macos,windows,linux --template=plugin_ffi native_add
cd native_add
C/C++ 源代码会被创建至 native_add/src
。这些源代码在不同平台构建时会生成在不同平台的构建文件夹。
FFI 库只能绑定 C 语言的符号,所以 C++ 语言的符号会被标记为 extern "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 Native APIs 列表中找到所需的库。它列出了稳定的本地 API。
-
使用
DynamicLibrary.open
加载库。示例:加载 OpenGL ES (v3):dartDynamicLibrary.open('libGLES_v3.so');
如果文档中有说明,你还需要根据说明更新 Android 应用程序或插件的清单文件。
第一方库
#对于应用程序或插件,以源代码或二进制形式包含本机代码的过程是相同的。
开源三方库
#遵循 Android 文档中的
添加 C 和 C++ 代码到项目
来添加本地代码和对本地代码工具链的支持(CMake 或 ndk-build
)。
闭源三方库
#要创建包含 Dart 源代码,但以二进制形式分发 C/C++ 库的 Flutter 插件,请按照如下说明:
-
打开你项目的
android/build.gradle
文件。 -
添加 aar 工件添加为依赖。 不要在你的 Flutter package 中导入工件。对应的,它需要在一个仓库中下载,比如 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">
Other Resources
#To learn more about C interoperability, check out these videos:
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-09-02。 查看文档源码 或者 为本页面内容提出建议。