安卓与Ublox NINA B1 BLE通知配置及数据丢包问题求助
正确设置Indication的方案
- 不要同时开启通知和指示:Ublox NINA B1的SPS服务FIFO特征值仅支持Notification,你当前写死
0x03同时开启两种模式的操作会导致外设配置失败,正确的做法是先判断特征值的属性再设置描述符值:- 若特征值含
BluetoothGattCharacteristic.PROPERTY_NOTIFY,描述符设为BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE(即0x01, 0x00) - 若含
BluetoothGattCharacteristic.PROPERTY_INDICATE,设为BluetoothGattDescriptor.ENABLE_INDICATION_VALUE(即0x02, 0x00)
- 若特征值含
- 移除延迟写描述符的逻辑:Android BLE栈要求所有GATT操作串行执行,靠延迟保证顺序不可靠,你需要在
onDescriptorWrite回调中监听配置结果,确认返回BluetoothGatt.GATT_SUCCESS后,再开始数据收发,否则说明通知未正常开启。 - 增加描述符存在性校验:你当前的代码没有判断
getDescriptor返回是否为null,若特征值不存在对应CCC描述符,后续操作会直接空崩。
正确的设置代码示例:
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } // 先设置本机的通知开关 boolean setSuccess = mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); if (!setSuccess) { Log.e(TAG, "开启本地通知失败"); return; } // 校验CCC描述符存在 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(getString(R.string.CLIENT_CHARACTERISTIC_CONFIG))); if (descriptor == null) { Log.e(TAG, "未找到CCC描述符"); return; } // 根据特征属性设置描述符值 if (enabled) { int props = characteristic.getProperties(); if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else if ((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); } else { Log.e(TAG, "特征值不支持通知/指示"); return; } } else { descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); } // 直接执行写描述符,不要加延迟,在回调中判断结果 mBluetoothGatt.writeDescriptor(descriptor); }
数据丢失的常见原因及修复方案
- GATT操作并发导致通知配置失败:你当前在
enableNotification方法中同时触发了读特征值、开通知两个GATT操作,Android BLE栈不支持并发GATT请求,会直接丢弃后续请求,导致通知未正常开启。需要将所有GATT操作加入串行队列,等上一个操作的回调返回后再执行下一个。 - SPS流控未实现:Ublox NINA B1的SPS服务依赖Credits特征值做流控,你需要每收到20字节数据就给外设返还一个Credit,否则外设的发送缓冲区满后会主动丢包,这是SPS场景下丢包的最常见原因。
- 接收逻辑缺陷:
- 你当前仅在
request_flag为true时才存储接收数据,若外设提前返回数据、或上一次请求的残留数据未处理,会直接丢弃有效数据。 - 你对
rx_Data执行Arrays.fill(rx_Data, (byte) 0)的操作会导致部分复用数组的Android BLE实现后续返回空数据,直接移除该行即可。 - 缓冲区缺少超时机制:如果只收到部分报文,既没达到预期长度也没超过长度,会一直占用缓冲区,导致后续接收的数据全部错位丢包,需要增加超时判断,超过100ms没收到完整报文就清空缓冲区。
- 你当前仅在
- 连接参数不合理:主动请求BLE连接间隔为10~20ms,避免手机或外设自动拉长连接间隔导致缓冲区溢出丢包。
- MTU未协商:虽然你当前报文小于20字节,但可以主动协商更大的MTU(比如247),减少分片概率,进一步降低丢包可能。
内容的提问来源于stack exchange,提问作者Marinus Snyman
相关产品推荐
相关产品推荐

