Flutter FFI 学习笔记系列
2021年3月份,官方宣布发布 Flutter 2.0 正式版,至此,FFI 终于不再是 beta 版本,而是正式稳定版。
FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi
库来调用 C 语言编写的代码。
在某些开发场景下,使用 FFI 比使用 Channel 要好很多,FFI 不需要 await
/ async
,代码更自然、更舒适。
官方 FFI 使用示例:https://flutter.cn/docs/development/platform-integration/c-interop 。
需要注意一点:dart:ffi
库只能用来调用 C 语言 API,不支持调用 C++ 语言 API,因此,C++的代码需要包装成 C 风格才行。
下面通过一个示例来演示 FFI 的基本使用步骤。
创建一个名称为 “native_add
” 的工程,含 android 和 ios 平台:
flutter create --platforms=android,ios native_add
cd native_add
新建 native_add.cpp
文件,位于 ios/Runner
cat ios/Runner/native_add.cpp
#include
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
可以将 C 代码放到 ios/Runner 目录下,因为 CocoaPods 不允许代码处于比 podspec 文件更高的目录层级,但是 Gradle 允许您指向 ios 文件夹。iOS 和 Android 可以共用一套C代码;当然,也可以将特定于 Android 的代码添加到 android 文件夹并修改 CMakeLists.txt 文件。
dart:ffi
只允许和 C 符号进行绑定,因此如果是 C++ 编写的符号,需要添加 extern C
标记。另外,还应该添加属性来表明符号是被 Dart 引用的,这样可以防止链接器在优化链接时会丢弃符号,这里说的属性就是上面的 __attribute__((visibility("default"))) __attribute__((used))
。
在 iOS 中,您需要告诉 Xcode 如何静态链接这个文件,步骤如下:
ios/Runner.xcworkspace
;native_add.cpp
源码文件到 Xcode 工程中,如下图所示; 在 Android 中,需要在创建一个 CMakeLists.txt
文件用来定义如何编译源文件,同时告诉 Gradle 如何去定位它们。
首先,在 android/app
目录中,创建 CMakeLists.txt
文件,内容如下:
cmake_minimum_required(VERSION 3.4.1) # for example
add_library( native_add
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
../../ios/Runner/native_add.cpp )
然后,打开 android/app/build.gradle
文件,添加以下内容:
android {
// ...
externalNativeBuild {
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "CMakeLists.txt"
}
}
// ...
}
最后,修改 android/app/local.properties
文件,添加一行代码 ndk.dir
,如下:
sdk.dir=/Users/clarkwain/Developer/android_sdk
ndk.dir=/Users/clarkwain/Developer/android-ndk-r21e
flutter.sdk=/Users/clarkwain/Developer/flutter
说明:
ndk.dir
表示指定NDK所在目录上面完成各个平台的配置之后,接着就可以在 Dart 中编写调用 C 的函数了。
首先,打开 lib/main.dart
文件,修改为:
import 'package:flutter/material.dart';
import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX
void main() {
runApp(FFIDemo());
}
class FFIDemo extends StatefulWidget {
const FFIDemo({Key key}) : super(key: key);
@override
_FFIDemoState createState() => _FFIDemoState();
}
class _FFIDemoState extends State {
DynamicLibrary nativeAddLib;
int Function(int x, int y) nativeAddFunc;
@override
void initState() {
super.initState();
//加载 C 符号
nativeAddLib = Platform.isAndroid
? DynamicLibrary.open("libnative_add.so")
: DynamicLibrary.process();
//查找"native_add"符号,并转为 Dart 函数
nativeAddFunc = nativeAddLib
.lookupFunction(Int32, Int32)>>("native_add")
.asFunction();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Text(
"1+1=${nativeAddFunc(1, 1)}", //调用 C 的 native_add() 函数
),
),
);
}
}
上面就是 Flutter FFI 的使用示例了,在后面的章节中,将会详细介绍 FFI 的使用,欢迎关注。