Flutter通过Dart FFI调用C++加载Assets模型的问题求助
Flutter中向C++传递深度学习模型文件的解决方案
一、能否直接通过路径让C++加载Flutter Asset?
不能直接用Asset路径让C读取,因为Flutter的Asset会被打包到应用的归档包(APK/IPA)中,并非文件系统中的真实路径,C的std::ifstream无法直接访问。但可以通过将Asset复制到应用沙盒目录的方式间接实现路径传递:
实现步骤
- 在Dart中将Asset内容写入应用沙盒的可访问文件
- 将沙盒中的文件路径传给C++,用
std::ifstream正常读取
Dart代码示例
import 'dart:io'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; Future<String> copyAssetToSandbox(String assetPath) async { final ByteData data = await rootBundle.load(assetPath); final Directory appDir = await getApplicationDocumentsDirectory(); final String filePath = '${appDir.path}/model_copy.nb'; final File file = File(filePath); await file.writeAsBytes(data.buffer.asUint8List()); return filePath; } // 使用示例 final String modelPath = await copyAssetToSandbox('assets/models/myModel.nb'); // 调用C++的init_model_from_file函数
C++代码示例
extern "C" __attribute__((visibility("default"))) __attribute__((used)) void init_model_from_file(const char* model_path) { std::ifstream file(model_path, std::ios::binary); if (file.is_open()) { // 读取模型逻辑 file.close(); } else { std::cerr << "Error opening the file!" << std::endl; } }
二、内存传递方式的问题修复
你当前的内存传递实现存在两个核心问题:未将ByteData内容拷贝到分配的指针、未传递数据长度(二进制文件可能包含'\0'导致std::string截断)。另外,My_Predictor*可以用Pointer<Void>在Dart中保存,后续调用时再强转即可,复杂继承结构不影响指针传递。
修正后的代码
Dart代码
import 'dart:async'; import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart' as ffi; // 修正函数签名:增加数据长度参数 typedef InitModelFunction = Pointer<Void> Function(Pointer<Uint8>, Int64); typedef InitModel = Pointer<Void> Function(Pointer<Uint8>, int); const String _libName = 'mylib'; final DynamicLibrary _dylib = DynamicLibrary.open('lib$_libName.so'); final InitModel initModel = _dylib.lookupFunction<InitModelFunction, InitModel>('init_model_from_buffer'); // 读取Asset并传递给C++ ByteData detModelContent = await rootBundle.load("assets/models/myModel.nb"); final Uint8List modelBytes = detModelContent.buffer.asUint8List(); // 分配内存并拷贝数据 Pointer<Uint8> detModelContentPtr = ffi.malloc.allocate<Uint8>(modelBytes.length); detModelContentPtr.asTypedList(modelBytes.length).setAll(0, modelBytes); print('detModelContent size in dart: ${modelBytes.length}'); // 调用C++函数,同时传递数据长度 final Pointer<Void> predictorPtr = initModel(detModelContentPtr, modelBytes.length); // 释放Dart侧分配的内存 ffi.malloc.free(detModelContentPtr);
C++代码
#include <string> // 假设My_Predictor的init方法接受const char*和长度参数 extern "C" __attribute__((visibility("default"))) __attribute__((used)) My_Predictor* init_model_from_buffer(const char* c_det_model_content, size_t data_len) { // 用数据长度构造std::string,避免'\0'截断 std::string det_model_content(c_det_model_content, data_len); LOGI("detModelContent size in cpp: %zu", det_model_content.size()); My_Predictor* my_predictor = new My_Predictor(); // 调整init方法,接受内存内容和长度(如果原来的init只接受路径,需要修改为支持内存加载) my_predictor->init(det_model_content.data(), det_model_content.size()); return my_predictor; } // 务必提供释放Predictor的接口,避免内存泄漏 extern "C" __attribute__((visibility("default"))) __attribute__((used)) void destroy_predictor(My_Predictor* predictor) { delete predictor; }
两种方式的选择
- 路径传递:适合大模型,避免内存占用过高,代码逻辑更直观,但需要额外的文件IO操作。
- 内存传递:性能更高(无文件IO),适合小模型,但需要注意内存管理和数据长度传递。
内容的提问来源于stack exchange,提问作者Ligcox
相关产品推荐
相关产品推荐

