线程下通过BLE向Nordic开发板发送数据的性能优化咨询
BLE向Nordic开发板发送数据功能优化方案
现有代码核心问题梳理
- 轮询+固定250ms休眠的等待逻辑大幅拉低发送吞吐量,无意义的空等占了大部分耗时
- alert计数到2就强制重置可发送状态的逻辑,会导致未收到应答就发起下一次发送,是偶发丢包的核心原因
- 循环没有增加连接状态判断、超时退出机制,蓝牙断连后会陷入死循环
- 每次调用sendData都新建线程,频繁创建销毁线程增加额外开销
- 未校验bluetoothGatt.writeCharacteristic的返回值,写入请求提交蓝牙栈失败时也会进入无意义的等待
具体优化方案
1. 替换轮询机制为事件驱动等待
用CountDownLatch替代原有AtomicBoolean做同步,避免无意义轮询,同时内置超时机制,避免死循环:
首先修改类成员变量:
// 替换原有AtomicBoolean getSendingState private CountDownLatch sendAckLatch; // 单次发送超时时间,可根据实际场景调整 private static final long SEND_TIMEOUT_MS = 1000; // 最大重试次数 private static final int MAX_SEND_RETRY = 3; // 复用发送线程,避免频繁创建新线程 private final ExecutorService sendExecutor = Executors.newSingleThreadExecutor();
修改发送逻辑:
public void sendData(final String text) { // 发送前先校验连接状态,异常直接返回 if (connectionState != STATE_CONNECTED || bluetoothGatt == null) { // 可自行增加发送失败回调通知上层 return; } // 仅在连接异常时调用refreshDeviceCache,不要每次发送都调用,避免蓝牙栈不稳定 // refreshDeviceCache(bluetoothGatt); this.text = text; BluetoothGattService bluetoothGattService = bluetoothGatt.getService(UUID.fromString(SampleGattAttributes.UUID_SERVICE_NORDIC)); final BluetoothGattCharacteristic characteristicRx = bluetoothGattService.getCharacteristic(UUID_RX_CHARACTERISTIC); byte[] tx = text.getBytes(StandardCharsets.UTF_8); characteristicRx.setValue(tx); sendExecutor.execute(() -> { int retryCount = 0; while (retryCount < MAX_SEND_RETRY) { sendAckLatch = new CountDownLatch(1); // 校验写入请求是否成功提交到蓝牙栈 boolean writeSubmitted = bluetoothGatt.writeCharacteristic(characteristicRx); if (!writeSubmitted) { retryCount++; continue; } try { // 等待应答,超时自动退出 if (sendAckLatch.await(SEND_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { // 发送成功直接结束 return; } else { // 超时重试 retryCount++; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } // 重试次数耗尽,触发发送失败回调 }); }
修改接收应答逻辑:
if (UUID_TX_CHARACTERISTIC.equals(characteristic.getUuid())) { Log.w("NOTIFICATION", Arrays.toString(characteristic.getValue())); intent.putExtra(EXTRA_DATA, Arrays.toString(characteristic.getValue())); byte[] bytes = characteristic.getValue(); incomingDataAsString = new String(bytes); // 收到应答后唤醒发送线程 if (sendAckLatch != null) { sendAckLatch.countDown(); } }
2. 额外性能优化点
- 分包处理:BLE默认MTU为23字节,单次最多发送20字节有效数据,如果发送的text长度超过20字节,需要自行实现分包逻辑,每个分包单独等待应答
- 协商更大MTU:GATT连接成功后调用
bluetoothGatt.requestMtu(512),和开发板协商更大的传输单元,减少发送次数,大幅提升吞吐量 - 增加断连监听:收到
ACTION_GATT_DISCONNECTED广播时直接终止所有待发送任务,清空发送队列,避免死循环 - 实现发送队列:如果有高频多次发送需求,不要每次调用sendData就单独发起任务,用队列缓存待发送数据,前一个发送完成后自动取下一个发送,避免多个发送任务冲突
内容的提问来源于stack exchange,提问作者Thomassek
相关产品推荐
相关产品推荐

