React Native应用中如何获取BLE设备实时更新的RSSI值
react-native-ble-manager@8.0.2的readRSSI方法本身存在原生层实现缺陷:首次调用时原生端会临时注册RSSI结果回调,拿到返回值后回调就被销毁,没有做重复绑定,后续JS层发起的调用虽然能传到原生蓝牙层,但结果无法透传回JS线程,表现为Promise一直挂起,既不返回有效值也不抛出错误。
另外两个常见的错误调用方式也会触发相同现象:
- 调用频率过高:BLE连接态下底层链路上报RSSI的最高频率仅为1次/秒,短时间连续调用会直接堵塞GATT操作队列,后续所有请求都会卡住
- 并发GATT操作:同一时间如果有服务发现、特征读写等其他GATT操作在执行,
readRSSI会被排到队列末尾,前面的操作一旦超时无响应,RSSI读取就会一直无返回
方案1:兼容8.0.2版本的轮询实现
不要用setInterval高频触发调用,改用递归轮询逻辑,两次调用间隔不小于800ms,每次调用前先确认设备连接状态,避免并发GATT操作:
// 组件内定义轮询定时器标记 rssiPollingTimer = null; startRSSIPolling = (peripheralId) => { // 启动前先清理已有定时器,避免重复创建轮询 if (this.rssiPollingTimer) clearTimeout(this.rssiPollingTimer); const poll = async () => { try { // 先校验设备连接状态,断连则直接停止轮询 const isConnected = await BleManager.isPeripheralConnected(peripheralId); if (!isConnected) { this.stopRSSIPolling(); return; } // 确保当前无其他GATT操作执行时再调用读取方法 const rssi = await BleManager.readRSSI(peripheralId); console.log("Current RSSI: ", rssi); // 此处写入RSSI更新的业务逻辑 } catch (err) { console.log("Read RSSI failed: ", err); } finally { // 无论读取成功失败,间隔800ms发起下一次调用 this.rssiPollingTimer = setTimeout(poll, 800); } }; // 启动首次轮询 poll(); }; stopRSSIPolling = () => { if (this.rssiPollingTimer) { clearTimeout(this.rssiPollingTimer); this.rssiPollingTimer = null; } };
注意:设备断连、页面/组件卸载时必须调用stopRSSIPolling清理定时器,避免内存泄漏和无效调用。如果Android端按照上述逻辑实现后仍然只有首次调用返回值,需要在业务层自行加锁保证同一时间只有一个GATT操作在执行——8.0.2版本的Android端没有实现GATT操作串行排队,并发调用必然会出现请求卡住的问题。
方案2:升级依赖版本(成本最低)
8.0.2版本的RSSI回调bug在8.1.0版本已经被官方修复,直接将依赖升级到react-native-ble-manager@^8.1.0及以上稳定版本,不需要做额外的特殊兼容,配合方案1的轮询逻辑即可稳定持续获取RSSI值。
方案3:替代实现思路
如果因为项目依赖限制无法升级版本,且方案1的兼容效果达不到预期,可以利用BLE广播特性实现:保持设备连接的同时开启低功耗扫描,通过扫描回调拿到的外设信息自带RSSI值,这个值的更新频率、准确率和readRSSI接口基本一致,且不会占用GATT操作队列。扫描时可以通过外设ID做过滤,只接收目标设备的广播包,不会产生额外的功耗开销。
内容的提问来源于stack exchange,提问作者Prasad Rokade

