Flutter中如何让指令发送类接收蓝牙管理器返回的数据?
解决方案:实现指令请求与响应的一对一绑定
核心思路是通过请求ID+Completer的方式,将每个发送的指令与对应的响应数据绑定,让发送类能直接await获取返回结果,同时兼容主动上报的数据处理。
1. 修改BluetoothManager类
添加请求映射表、超时处理,将接收的数据分发到对应的请求发起方:
import 'dart:async'; import 'dart:typed_data'; import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; class BluetoothManager extends ChangeNotifier { static final BluetoothManager _singleton = BluetoothManager._internal(); factory BluetoothManager() => _singleton; BluetoothManager._internal(); BluetoothConnection? connection; // 存储待处理请求:key为请求唯一标识,value为对应的异步完成器 final Map<String, Completer<List<dynamic>>> _pendingRequests = {}; // 请求超时时间(可根据设备响应速度调整) final Duration _requestTimeout = const Duration(seconds: 5); List<dynamic> globalFinalList = []; Future<void> connectToExternalDevice(BluetoothDevice device) async { try { connection = await BluetoothConnection.toAddress(device.address); connection?.input?.listen(_onDataReceived).onDone(() { // 连接断开时,终止所有待处理请求 _pendingRequests.values.forEach((completer) { if (!completer.isCompleted) { completer.completeError('蓝牙连接已断开'); } }); _pendingRequests.clear(); }); } catch (exception) { rethrow; } } /// 发送指令并等待对应响应 Future<List<dynamic>> sendCommand(Uint8List commandBytes, String requestId) async { final completer = Completer<List<dynamic>>(); _pendingRequests[requestId] = completer; // 设置超时逻辑 Future.delayed(_requestTimeout, () { if (!completer.isCompleted) { completer.completeError('请求超时'); _pendingRequests.remove(requestId); } }); // 发送指令 try { if (connection == null) throw Exception('未连接蓝牙设备'); connection!.output.add(commandBytes); await connection!.output.allSent; } catch (e) { completer.completeError(e); _pendingRequests.remove(requestId); rethrow; } return completer.future; } void _onDataReceived(Uint8List data) { // 解析返回数据中的请求标识(必须根据你的蓝牙通信协议实现) String? responseRequestId = _parseRequestIdFromResponse(data); if (responseRequestId != null && _pendingRequests.containsKey(responseRequestId)) { // 找到对应的请求,返回数据 final completer = _pendingRequests.remove(responseRequestId); if (completer != null && !completer.isCompleted) { List<dynamic> processedData = _processReceivedData(data); completer.complete(processedData); } } else { // 无匹配请求,视为设备主动上报数据,走全局通知逻辑 globalFinalList = _processReceivedData(data); notifyListeners(); } } /// 从返回数据中提取请求标识(需根据你的协议自定义) /// 示例:假设返回数据第一个字节是指令响应标识,与发送的指令标识一致 String? _parseRequestIdFromResponse(Uint8List data) { if (data.isEmpty) return null; // 这里替换成你实际的协议解析逻辑 return data.first.toRadixString(16); } /// 处理收到的数据为业务需要的格式 List<dynamic> _processReceivedData(Uint8List data) { // 替换成你原来的数据处理逻辑 return Uint8List.fromList(data).toList(); } }
2. 修改设备固件类
让sendMessage返回Future,调用BluetoothManager的sendCommand方法并传入请求ID:
class DeviceFirmware { final BluetoothManager _bluetoothManager = BluetoothManager(); Future<List<dynamic>> sendMessage() async { var commandhelper = CommandsHelper(); var finalCommandWithCRC = await commandhelper.getCommandWithCRC(commandhelper.mainFirmwareCommand); var finalHexValues = await commandhelper.convertCommandToHexTwoBytes(finalCommandWithCRC); var finalCommandtoSend = await commandhelper.convertCommandToSignedDecimal(finalHexValues); Uint8List bytes = Uint8List.fromList(finalCommandtoSend); // 根据指令生成唯一请求ID(需与返回数据的标识匹配) String requestId = _generateRequestId(bytes); // 发送指令并等待返回结果 return await _bluetoothManager.sendCommand(bytes, requestId); } /// 根据指令生成请求ID(需与_parseRequestIdFromResponse逻辑对应) String _generateRequestId(Uint8List commandBytes) { if (commandBytes.isEmpty) throw Exception('指令为空'); // 示例:用指令第一个字节作为ID return commandBytes.first.toRadixString(16); } }
3. 使用示例
在业务代码中调用设备固件类的sendMessage,直接获取返回数据:
void fetchFirmwareData() async { try { DeviceFirmware firmware = DeviceFirmware(); List<dynamic> responseData = await firmware.sendMessage(); // 处理返回的数据 print('收到固件响应:$responseData'); } catch (e) { // 处理错误(超时、连接断开等) print('请求失败:$e'); } }
关键注意事项
- 必须确保你的蓝牙通信协议支持指令与响应的一一识别(比如每个指令对应唯一的响应标识),这是实现该方案的前提
- 若存在多并发请求,Map的操作在Flutter单线程环境下无需额外锁,若涉及复杂异步场景可使用
synchronized包保证线程安全 - 超时时间需根据设备实际响应速度调整,避免误判超时
内容的提问来源于stack exchange,提问作者Microcad
相关产品推荐
相关产品推荐

