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

Riverpod StreamProvider异常:AsyncValue始终处于Loading状态求助

问题分析与解决方案

核心问题原因

你的代码主要存在以下几个关键问题:

  1. 重复创建ProtoReceiveMessageChannel实例导致消息处理器被覆盖
    StreamProvider的回调会在订阅触发/重新触发时重复执行,每次都会创建新的ProtoReceiveMessageChannel实例。新实例会覆盖同一个channel name的消息处理器,而Widget订阅的是早期实例的流,因此无法收到后续事件。

  2. 冗余的消息处理器设置
    构造函数中已经调用了_binaryMessenger.setMessageHandler,_onListen方法中又重复执行该操作,虽然不会直接报错,但属于无效逻辑。

  3. 缺少资源清理与异常捕获
    没有在实例销毁时关闭StreamController和移除消息处理器,存在内存泄漏风险;同时onMessage方法未捕获解码异常,若解码失败会导致事件丢失且无报错信息。

修复方案

1. 持久化ProtoReceiveMessageChannel实例

通过Provider确保全局只有一个ProtoReceiveMessageChannel实例,避免重复覆盖消息处理器:

final appWindowStateChannelProvider = Provider<ProtoReceiveMessageChannel<AppWindowState>>((ref) {
  const channelName = 'u/AppWindowState';
  final channel = ProtoReceiveMessageChannel<AppWindowState>(
    channelName,
    ProtoDecoder<AppWindowState>((bytes) => AppWindowState.fromBuffer(bytes)),
  );
  // 销毁时清理资源
  ref.onDispose(() {
    ServicesBinding.instance.defaultBinaryMessenger.setMessageHandler(channelName, null);
    channel._streamController.close();
  });
  return channel;
});

final appWindowStateProvider = StreamProvider<AppWindowState>((ref) {
  final channel = ref.watch(appWindowStateChannelProvider);
  return channel.messageStream;
});

2. 优化ProtoReceiveMessageChannel类

移除冗余逻辑,添加异常捕获:

class ProtoReceiveMessageChannel<T extends GeneratedMessage> {
  final String name;
  final BinaryMessenger _binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
  final ProtoDecoder<T> _receiveCodec;
  late StreamController<T> _streamController;

  ProtoReceiveMessageChannel(this.name, this._receiveCodec) {
    _streamController = StreamController<T>.broadcast();
    _binaryMessenger.setMessageHandler(name, onMessage);
  }

  Stream<T> get messageStream => _streamController.stream;

  Future<ByteData?>? onMessage(ByteData? message) {
    try {
      if (message == null) return null;
      final decodedMsg = _receiveCodec.decodeMessage(message);
      if (decodedMsg != null) {
        _streamController.add(decodedMsg);
      }
    } catch (e, stack) {
      debugPrint('消息解码失败: $e\n$stack');
      _streamController.addError(e, stack);
    }
    return null;
  }
}

3. 额外验证点

  • 确认外部事件发送的channel name与'u/AppWindowState'完全一致,无拼写错误
  • 若流始终无初始事件,可考虑在ProtoReceiveMessageChannel初始化时主动发送一次初始状态,避免Widget一直处于loading状态

内容的提问来源于stack exchange,提问作者Elad Maimoni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:59:56