Flutter FFI Study Notes Series
- "The Simplest Example of Flutter FFI"
- "Flutter FFI Basic Data Type"
- "Flutter FFI Function"
- "Flutter FFI String"
- "Flutter FFI Structure"
- "Flutter FFI Class"
- "Flutter FFI Array"
- "Flutter FFI Memory Management"
- 《Flutter FFI Dart Native API》
In the previous chapters, basic data types, strings, structures, etc. were introduced, and then the arrays in FFI are introduced.
1. Array representation
Arrays in C language can be int[]
represented by or by int*
. In Dart, is generally used to Pointer<T extends NativeType>
represent an array in C language.
If a pointer is used to represent an array in C language, the length of the array generally needs to be specified. For example:
typedef struct {
int32_t *data;
int32_t length;
} IntArray;
IntArray *createIntArray() {
auto *result = (IntArray *) malloc(sizeof(IntArray));
result->length = 5;
result->data = (int32_t *) malloc(result->length * sizeof(int32_t));
for (int i = 0; i < result->length; i++) {
result->data[i] = i * 2;
}
return result;
}
Code description:
- In the above code, an integer array is represented by defining
IntArray
a structure ,data
representing data andlength
length;
In Dart, arrays are Pointer<T extends NativeType>
represented , for example, int32_t
arrays are Pointer<Int32>
represented by .
dart:ffi
Some extension functions are defined in to help us use it Pointer<T extends NativeType>
as an array, for example Pointer<Int32>
:
extension Int32Pointer on Pointer<Int32> {
//通过下标读取数组元素
external int operator [](int index);
//通过下标改写数组元素
external void operator []=(int index, int value);
//将指针数组转为List,可以像List一样使用各种便捷函数,例如:forEach
//需要注意的是,使用时不能超出数组范围
external Int32List asTypedList(int length);
}
illustrate:
Int32Pointer
[]
In the extension function, the and[]=
operator functions are defined , which are used to read or rewrite the value of the specified index of the array;asTypedList()
Functions can convert arrays to DartList
so thatList
various .
2. Example of array usage
The following example demonstrates basic usage of arrays.
First, define the array structure, methods for creating arrays, and methods for accessing arrays in C:
#include <malloc.h>
#include <cstring>
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
//定义IntArray结构体
typedef struct {
int32_t *data;
int32_t length;
} IntArray;
//创建数组
DART_API IntArray *createIntArray() {
auto *result = (IntArray *) malloc(sizeof(IntArray));
result->length = 5;
result->data = (int32_t *) malloc(result->length * sizeof(int32_t));
for (int i = 0; i < result->length; i++) {
result->data[i] = i * 2;
}
return result;
}
//获取数组最大值
DART_API int32_t getMaxInt(const int32_t *intArray, int32_t length) {
int32_t max = intArray[0];
for (int i = 1; i < length; i++) {
if (intArray[i] > max) {
max = intArray[i];
}
}
return max;
}
//定义结构体Point,表示平面上的一点
typedef struct {
int32_t x;
int32_t y;
} Point;
//获取若干几点
DART_API Point *getPoints(int32_t length) {
auto *points = (Point *) malloc(length * sizeof(Point));
for (int i = 0; i < length; ++i) {
points[i].x = 100 * (i + 1) + i;
points[i].y = 200 * (i + 1) + (i + 1);
}
return points;
}
Then, define the corresponding structure and function types in Dart:
//对应C中的IntArray结构体
class IntArray extends Struct {
external Pointer<Int32> data;
@Int32()
external int length;
}
//对应C中的Point结构体
class Point extends Struct {
@Int32()
external int x;
@Int32()
external int y;
String toDebugString() => "{x=$x, y=$y}";
}
typedef Native_createIntArray = Pointer<IntArray> Function();
typedef FFI_createIntArray = Pointer<IntArray> Function();
typedef Native_getMaxInt = Int32 Function(Pointer<Int32>, Int32);
typedef FFI_getMaxInt = int Function(Pointer<Int32>, int);
typedef Native_getPoints = Pointer<Point> Function(Int32 length);
typedef FFI_getPoints = Pointer<Point> Function(int length);
Finally, write the interface that calls the array in Dart:
//加载符号
DynamicLibrary nativeApi = Platform.isAndroid
? DynamicLibrary.open("libnative_ffi.so")
: DynamicLibrary.process();
//查找函数符号 - createIntArray
FFI_createIntArray createIntArrayFunc =
nativeApi.lookupFunction<Native_createIntArray, FFI_createIntArray>(
"createIntArray");
//查找函数符号 - getMaxInt
FFI_getMaxInt getMaxIntFunc =
nativeApi.lookupFunction<Native_getMaxInt, FFI_getMaxInt>("getMaxInt");
//查找函数符号 - getPoints
FFI_getPoints getPointsFunc =
nativeApi.lookupFunction<Native_getPoints, FFI_getPoints>("getPoints");
//创建IntArray
Pointer<IntArray> pIntArray = createIntArrayFunc();
IntArray intArray = pIntArray.ref;
//打印数组元素
for (int i = 0; i < intArray.length; i++) {
print("intArray[$i]=${intArray.data[i]}");
}
//获取数组中的最大值
int max = getMaxIntFunc(intArray.data, intArray.length);
print("max of intArray=$max");
//将Int数组转为List使用
Int32List int32list = intArray.data.asTypedList(intArray.length);
int32list.forEach((it) => print("int32list=$it"));
//创建结构体数组
int length = 3;
Pointer<Point> pPointArray = getPointsFunc(length);
for (int i = 0; i < length; i++) {
print("points[$i]=${pPointArray[i].toDebugString()}");
}
//释放内存
calloc.free(intArray.data);
calloc.free(pIntArray);
calloc.free(pPointArray);
The output of the above code is as follows:
I/flutter ( 9388): intArray[0]=0
I/flutter ( 9388): intArray[1]=2
I/flutter ( 9388): intArray[2]=4
I/flutter ( 9388): intArray[3]=6
I/flutter ( 9388): intArray[4]=8
I/flutter ( 9388): max of intArray=8
I/flutter ( 9388): int32list=0
I/flutter ( 9388): int32list=2
I/flutter ( 9388): int32list=4
I/flutter ( 9388): int32list=6
I/flutter ( 9388): int32list=8
I/flutter ( 9388): points[0]={x=100, y=201}
I/flutter ( 9388): points[1]={x=201, y=402}
I/flutter ( 9388): points[2]={x=302, y=603}
3. Arrays of other types
Int32
The array and array are demonstrated above Struct
, and other types of arrays, such as: Double
, Float
, Int8
... are almost the same in usage.
4. Processing of zero-length dynamic arrays
The interaction between C language zero-length dynamic array and Dart directly gives the code:
C code:
#include "dart_api/dart_api.h"
#include "dart_api/dart_native_api.h"
#include "dart_api/dart_api_dl.h"
typedef struct {
int len;
int buffer[0];
} DynamicArray;
// 该函数用于创建一个 DynamicArray,给 Dart 使用。
DART_EXPORT DynamicArray *createArray(int len) {
auto arr = (DynamicArray *) calloc(sizeof(DynamicArray) + sizeof(int) * len, 1);
arr->len = len;
return arr;
}
Dart code:
void main() {
runApp(Demo()); }
class Demo extends StatefulWidget {
const Demo({
Key? key}) : super(key: key);
@override
_DemoState createState() => _DemoState();
}
const int intMaxValue = 9223372036854775807;
class DynamicArray extends Struct {
@Int32()
external int len;
///给 dimension1 参数赋一个很大的值,绕过 ffi-patch.dart 的 _checkIndex() 的检查
@Array(intMaxValue)
external Array<Int32> data;
}
typedef Native_Dart_CreatArray = Pointer<DynamicArray> Function(Int32);
typedef FFI_Dart_CreatArray = Pointer<DynamicArray> Function(int);
class _DemoState extends State<Demo> {
late DynamicLibrary nativeApi;
@override
void initState() {
super.initState();
testNative();
}
@override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Center(child: Text("FFI Demo"))));
}
void testNative() {
nativeApi = Platform.isAndroid
? DynamicLibrary.open("libnative_ffi.so")
: DynamicLibrary.process();
FFI_Dart_CreatArray createArray =
nativeApi.lookupFunction<Native_Dart_CreatArray, FFI_Dart_CreatArray>(
"createArray");
Pointer<DynamicArray> array = createArray(10);
print("array len=${array.ref.len}");
for (int i = 0; i < array.ref.len; i++) {
array.ref.data[i] = i * 2;
}
for (int i = 0; i < array.ref.len; i++) {
final int a = array.ref.data[i];
print("i=$a");
}
}
}
illustrate
Array
When defining in dart , the length of the array must be specified, and it must be a positive number. If it is, an error will be0
reported and the compilation will fail;- The problem lies in how to solve the problem that the data length is dynamically changing. The solution here is to
dimension1
set a large value for to bypass the check that bypasses theffi-patch.dart
._checkIndex()
- But in this way, arrays cannot be created in Dart, and C needs to provide a function to create arrays——
createArray()
;
Program running result:
I/flutter (25532): array len=10
I/flutter (25532): i=0
I/flutter (25532): i=2
I/flutter (25532): i=4
I/flutter (25532): i=6
I/flutter (25532): i=8
I/flutter (25532): i=10
I/flutter (25532): i=12
I/flutter (25532): i=14
I/flutter (25532): i=16
I/flutter (25532): i=18
5. Summary
The mutual calls between arrays in Dart and C are introduced above. It needs to be explained: there is actually another way to create an array, which is to use malloc
and calloc
. In the next chapter, I will introduce FFI memory management, and also introduce how malloc
Dart calloc
creates arrays through and, welcome to pay attention.