You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用dart:FFI时C++库向Flutter传递数据异常问题咨询

Dart FFI调用C++回调传递条码数据异常的排查与修复

问题背景

使用Flutter的dart:ffi对接C条码扫描库,C端负责监听扫描事件:

  1. 将条码数据写入.txt文件(数据始终正确)
  2. 通过回调将数据传递给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:18:18