Flutter Dart FFI Pointer<Uint8>类型如何转成数组或String

前言

继上一次发布的 Flutter 直接调用so动态库,或调用C/C++源文件内函数 内容,最终我选择了第二种方式,直接把整个 Native C++ 的项目源代码放进了 Flutter 工程里编译(放在iOS的目录是因为它不支持自定义源码路径,Android是可以的)。这样的好处是 Android 和 iOS 两个平台都不需要分别再去写原生代码调用 .so 文件,也不用关注动态库平台,而且改动更方便。

编译时 Android 侧会生成指定平台的 .so 文件(Gradle里配置),iOS 侧会生成指定平台的 .a 文件(XCode里配置)

背景

Flutter 的项目里需要调用 C++ 进行 APDU 指令的操作和传输,APDU 也就是一串 16 进制编码的字符串,也就是说 出入参 需要传输 非短整型的值。实际传输需要根据 C++ 端的定义去使用 Flutter 端的数据类型。



一、代码部分

1. 分析底层定义函数的出入参找出映射类型

例如:C++ 定义了如下函数

c 复制代码
extern "C" short apduProcess(uint8_t *apdu, uint8_t *response, uint8_t *len) {
	... apdu & response 业务处理
	return 0;
}

我们的关键点就是分析 C++ 定义的 出入参 的数据类型映射,参考 Dart - C 数据类型映射表 可以得出结论:

  1. 函数出参 用于返回函数执行结果,使用的 short 类型,对应 Dart NativeType 的 Int16 类型;
  2. 函数入参有三个 用于数据的输入和输出,使用的 uint8_t 类型,对应 Dart NativeType 的 Uint8 类型,需要注意的是三个参数均为指针类型,由于 FFI 无法直接传输数组,因此需要需要自行开辟空间进行存取,类型为 使用 Pointer<Uint8>

于是 Flutter 端得到了如下函数:

dart 复制代码
typedef NativeFunc = 
	Int16 Function(Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>);
	
typedef FFIFunc = 
	int Function(Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>);

2. 调用函数

dart 复制代码
final func = _library.lookupFunction<NativeFunc, FFIFunc>('apduProcess');

// 初始化三个指针参数
final apdu = [0x00, 0xA4, 0x04, ...];
final inputApduPointer = calloc<Uint8>(apdu.length);
inputApduPointer.asTypedList(apdu.length).setAll(0, apdu);

final respApduPointer = calloc<Uint8>(100);
final respLenPointer = calloc<Uint8>(2);

// 调用函数
final result = func(inputApduPointer, respApduPointer, respLenPointer);

// 需要在指针释放之前把response值取出来
String respApduStr = '';
for (var index = 0; index < respLenPointer.value; index++) {
  // 由于我传入的内容是 16 进制字符串,所以需要 .toRadixString(16).padLeft(2, '0')
  respApduStr += respApduPointer.elementAt(index).value.toRadixString(16).padLeft(2, '0');
}

// 释放申请的内存空间
calloc.free(inputApduPointer);
calloc.free(respApduPointer);
calloc.free(respLenPointer);

3. 结论

遍历 respApduPointer.elementAt(index).value 即可将正确的值取出

而这两种方式拿到的值是不对的:

dart 复制代码
// 错误方式 1
respApduPointer.cast<Utf8>().toDartString()

// 错误方式 2
respApduPointer.asTypedList(xxx).map((e) => e.toRadixString(16).padLeft(2, '0')).join('')

二、分析一下我走的弯路

重点看 final respApduPointer = calloc<Uint8>(100); 这个变量,实际上它是给到 C++ 存数据的,所以我们取值前也需要在释放内存之前。它的类型是:Pointer<Uint8>,实际上也就这几个方法和属性:

这个变量实际存的是 C++ 的 uint8_t *response,是一个指针数组(由n个指针类型元素组成的一个数组),而不是一个数组指针(数组变量是一个指针类型),上面列举的错误方式其实操作的是指针变量,而 elementAt 取到的才是指针数组里的单个元素的指针

相关推荐
沐泽Mu1 小时前
嵌入式学习-QT-Day05
开发语言·c++·qt·学习
szuzhan.gy2 小时前
DS查找—二叉树平衡因子
数据结构·c++·算法
火云洞红孩儿2 小时前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
FeboReigns3 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns3 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
zh路西法4 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
.Vcoistnt4 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
小k_不小4 小时前
C++面试八股文:指针与引用的区别
c++·面试
沐泽Mu4 小时前
嵌入式学习-QT-Day07
c++·qt·学习·命令模式
ALISHENGYA4 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论