Flutter web热重启后Stream.listen残留 多监听并行问题咨询
问题原因
- Flutter热重启只会重置Dart层面的业务状态,WebSocket属于操作系统/浏览器层面的网络资源,默认不会跟随Dart VM重启自动销毁。Web端100%复现是因为Chrome会持久化持有已建立的WebSocket连接,旧连接的事件回调仍然会被浏览器事件循环触发;移动端偶现是因为移动OS会主动回收部分后台闲置的旧网络连接。
- 代码中没有存储
Stream.listen()返回的StreamSubscription实例,也没有在组件销毁时主动取消监听、关闭WebSocket连接,导致旧的回调引用一直被持有。 - 你使用的2.5.2版本Flutter Web本身存在热重启阶段外部资源清理逻辑的缺陷,未对未关闭的Socket连接做强制回收。
解决方案
1. 基础资源生命周期管理
在State中存储订阅实例和连接实例,重写dispose方法主动销毁资源,这是必做的常规操作,修改代码如下:
class _MyHomePageState extends State<MyHomePage> { var test = 0; late int randomInt; var random = Random(); var uri = Uri.parse('wss://ws-feed-public.sandbox.pro.coinbase.com'); late WebSocketChannel channel; // 新增:存储订阅实例 late StreamSubscription _wsSubscription; String request = json.encode({ "type": "subscribe", "product_ids": ["BTC-USD"], "channels": ["heartbeat"], }); @override void initState() { randomInt = random.nextInt(1000); channel = WebSocketChannel.connect(uri); channel.sink.add(request); // 保存订阅实例 _wsSubscription = channel.stream.listen((event) { print('$randomInt: $test'); test = test + 1; }); super.initState(); } // 新增:重写dispose方法销毁资源 @override void dispose() { _wsSubscription.cancel(); channel.sink.close(); super.dispose(); } @override Widget build(BuildContext context) => Container(); }
2. 针对热重启场景补充清理逻辑
热重启时组件的dispose不会被触发,可以重写State的didReassemble方法(仅Debug模式下热重启会触发),在该方法中主动清理旧资源:
@override void didReassemble() { // 热重启时先销毁旧连接 _wsSubscription.cancel(); channel.sink.close(); // 可选:如果需要重启连接可以在这里重新初始化 super.didReassemble(); }
3. 进阶优化
如果项目中大量使用WebSocket,可以封装全局单例的WebSocket管理类,统一处理连接的建立、销毁、重连逻辑,避免多个页面重复创建连接导致的资源泄漏。
内容的提问来源于stack exchange,提问作者pcrady
相关产品推荐
相关产品推荐

