使用dart:FFI时C++库向Flutter传递数据异常问题咨询
Dart FFI调用C++回调传递条码数据异常的排查与修复
问题背景
使用Flutter的dart:ffi对接C条码扫描库,C端负责监听扫描事件:
- 将条码数据写入.txt文件(数据始终正确)
- 通过回调将数据传递给Dart端显示
但回调传参存在三种异常:
- 数据为空
- 数据正确
- 首字符缺失(控制台显示缺失,UI显示
[])
条码为CODE128格式(如LOREM;IPSUM),无特殊字符,目前临时方案是让Dart回调读取文件数据,需解决直接回调传参的异常问题。
核心原因:内存生命周期不匹配
C++端传递给Dart的const char*指针,指向的是扫描器返回的临时栈内存/内部缓冲区。虽然写入文件时数据有效,但Dart回调执行存在异步延迟,当Dart读取指针指向的内存时,原内存已被扫描器回收或覆盖,导致数据错乱。此外,Dart端NativeCallable的引用管理不当,也可能引发回调逻辑异常。
修复方案
1. C++端:传递稳定内存的字符串拷贝
不要直接传递扫描器返回的临时指针,而是分配独立内存拷贝数据,确保Dart读取时内存有效:
#include <cstring> #include <cstdlib> // 全局回调函数指针 void (*callbackFunc)(const char*) = nullptr; void invokeBarcodeCallback(const char* barcodeData) { if (callbackFunc != nullptr) { std::string data = barcodeData; logMessage("invokeBarcodeCallback: " + data); // 分配内存并拷贝数据,内存生命周期由Dart端负责释放 char* copiedData = (char*)malloc(data.size() + 1); strcpy(copiedData, barcodeData); // 传递拷贝后的稳定指针 callbackFunc(copiedData); } }
2. Dart端:正确管理NativeCallable与内存释放
- 保持
NativeCallable的全局引用,避免被Dart GC回收 - 在回调中读取完字符串后,释放C++端分配的内存:
import 'dart:ffi'; import 'package:ffi/ffi.dart'; typedef InitializeScannerFunc = Void Function(Pointer<NativeFunction<BarcodeCallback>>); typedef InitializeScanner = void Function(Pointer<NativeFunction<BarcodeCallback>>); typedef BarcodeCallback = Void Function(Pointer<Utf8>); // 全局保存NativeCallable引用,防止被GC回收 late final NativeCallable<BarcodeCallback> nativeCallable; void barcodeCallback(Pointer<Utf8> barcodePtr) { if (barcodePtr.address == 0) { print("条码数据为空"); return; } // 转换为Dart字符串 final String barcode = barcodePtr.toDartString(); print("收到条码:$barcode"); // 释放C++端分配的内存 calloc.free(barcodePtr); } // 初始化扫描器(避免重复注册) bool _isScannerInitialized = false; void initScanner(DynamicLibrary dylib) { if (_isScannerInitialized) return; final InitializeScanner initializeScannerFunc = dylib.lookup<NativeFunction<InitializeScannerFunc>>('InitializeScanner').asFunction(); nativeCallable = NativeCallable<BarcodeCallback>.listener(barcodeCallback); initializeScannerFunc(nativeCallable.nativeFunction); _isScannerInitialized = true; } // UI使用示例 FloatingActionButton( onPressed: () { initScanner(dylib); // 假设dylib已提前加载 }, ),
3. 额外注意:避免重复注册回调
添加初始化状态判断,防止多次点击按钮重复注册回调,导致多份回调指针被保存,引发逻辑混乱。
内容的提问来源于stack exchange,提问作者SOVERFLOW
相关产品推荐
相关产品推荐

