You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 10:24:04