Flutter中bluetooth_classic包蓝牙经典设备扫描的订阅取消问题
问题解决:bluetooth_classic扫描时Stream重复监听报错
问题原因
onDeviceDiscovered()返回的是单订阅流(Single-Subscription Stream),这类流只能被监听一次,即便取消了之前的订阅,也无法复用同一个流实例再次监听,这就是再次扫描时出现Stream has already been listened to错误的核心原因。
解决方案
1. 调整订阅变量的可空性
将订阅变量声明为可空类型,方便后续重置:
StreamSubscription<Device>? _scanResultsSubscription;
2. 封装扫描启停方法,确保每次扫描获取新流
每次启动扫描前先停止已有扫描,再重新获取流并创建新订阅:
// 启动扫描 Future<void> startScan() async { // 先停止当前扫描(如果存在) await stopScan(); // 重新获取流实例并创建新订阅 _scanResultsSubscription = _bluetoothClassicPlugin.onDeviceDiscovered().listen( (event) { _discoveredDevices = [..._discoveredDevices, event]; }, onError: (err) { // 按需处理监听错误 }, onDone: () { // 流结束后重置订阅变量 _scanResultsSubscription = null; }, ); // 调用包的启动扫描API(根据实际包方法调整) await _bluetoothClassicPlugin.startScan(); } // 停止扫描 Future<void> stopScan() async { // 检查订阅是否有效,避免重复取消 if (_scanResultsSubscription != null && !_scanResultsSubscription!.isCanceled) { await _scanResultsSubscription!.cancel(); _scanResultsSubscription = null; } // 调用包的停止扫描API(根据实际包方法调整) await _bluetoothClassicPlugin.stopScan(); // 清空已发现设备(可选,根据业务需求) _discoveredDevices.clear(); }
3. 优化dispose方法
确保页面销毁时安全取消订阅:
@override void dispose() { _scanResultsSubscription?.cancel(); super.dispose(); }
关键注意点
- 不要复用同一个流实例,每次扫描都要通过
onDeviceDiscovered()获取新的流。 - 取消订阅后务必将
_scanResultsSubscription置为null,避免后续对已取消的订阅进行操作。 - 启停扫描都要异步处理,确保操作顺序执行,避免竞态问题。
内容的提问来源于stack exchange,提问作者Anjali Jagtap
相关产品推荐
相关产品推荐

