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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 02:04:56