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

Flutter SIP通话应用:推送唤醒后关联Call对象的方案咨询

解决dart-sip-ua推送唤醒后无法关联通话的方案

针对你遇到的推送唤醒后拿不到Call对象、无法关联通话的问题,下面是几个可行的实践方案:

1. 用SIP标准Call-ID替代客户端生成的CallUUID

客户端生成的CallUUID是内存级标识,进程重启后就会丢失,且服务器无法提前获取。但SIP通话的Call-ID是协议层面的唯一标识,服务器在发起/转接通话时就能拿到,完全可以放到推送payload里发给客户端:

  • 推送消息中携带Call-ID、通话方向(呼入/呼出)、对方号码等元数据;
  • 应用唤醒后,先把Call-ID存在本地存储(比如SharedPreferences);
  • 初始化SIPUAHelper并主动触发注册(而非等待自动重连),注册成功后:
    • 呼入场景:服务器会重新发送INVITE请求,客户端触发onCallReceived事件,此时对比新Call对象的Call-ID和本地存储的ID,匹配后绑定到CallScreen;
    • 呼出场景:遍历sipUAHelper.getCalls()列表,找到Call-ID匹配的Call对象即可。

2. 先渲染临时UI,后台自动关联Call对象

不用等Call对象完全获取再显示CallScreen,先利用推送携带的元数据渲染基础UI(比如显示对方号码、“连接中”状态),让用户有即时反馈,后台再默默处理SIP重连和Call对象关联:

  • 收到推送后直接跳转到CallScreen,用推送里的元数据初始化UI状态;
  • 在CallScreen的initState中主动调用sipUAHelper.register()加速重连;
  • 监听SIP注册成功事件和通话事件,一旦匹配到对应的Call对象,立即更新UI,替换临时状态并绑定通话操作(接听/挂断)。

3. 持久化通话关键信息到本地

在通话初始化(发起/收到)时,把Call的Call-ID、UUID、通话状态、对方信息等持久化到本地数据库或SharedPreferences:

  • 发起通话时:创建Call对象后立刻存储其关键信息;
  • 收到通话时:在onCallReceived触发时存储Call对象信息;
  • 应用唤醒后:先从本地读取这些信息,待SIP重连完成后,用SIPUAHelper.findCall()或遍历通话列表匹配,找到对应的Call对象;
  • 通话结束后,及时清理本地存储的通话信息,避免后续误匹配。

代码示例:推送唤醒后的Call对象关联

// 处理推送消息
void handlePushNotification(Map<String, dynamic> payload) {
  final String callId = payload['call_id'];
  final String remoteNumber = payload['remote_number'];
  
  // 存储待关联的Call-ID
  SharedPreferences.getInstance().then((prefs) {
    prefs.setString('pending_call_id', callId);
    prefs.setString('pending_remote_number', remoteNumber);
  });
  
  // 直接跳转到CallScreen
  Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => CallScreen())
  );
}

// CallScreen实现
class CallScreen extends StatefulWidget {
  @override
  _CallScreenState createState() => _CallScreenState();
}

class _CallScreenState extends State<CallScreen> {
  Call? _activeCall;
  String? _pendingCallId;
  String? _remoteNumber;
  late SharedPreferences _prefs;

  @override
  void initState() {
    super.initState();
    _initLocalData();
    _setupSipListeners();
    // 主动触发SIP注册,加速重连
    sipUAHelper.register();
  }

  void _initLocalData() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _pendingCallId = _prefs.getString('pending_call_id');
      _remoteNumber = _prefs.getString('pending_remote_number');
    });
  }

  void _setupSipListeners() {
    // 监听呼入通话事件
    sipUAHelper.onCallReceived.listen((Call call) {
      if (call.id == _pendingCallId) {
        _bindActiveCall(call);
      }
    });

    // 监听SIP注册成功事件
    sipUAHelper.onRegistered.listen((_) {
      // 遍历现有通话,匹配Call-ID
      for (final call in sipUAHelper.getCalls()) {
        if (call.id == _pendingCallId) {
          _bindActiveCall(call);
          break;
        }
      }
    });

    // 监听通话结束事件,清理本地数据
    sipUAHelper.onCallEnded.listen((Call call) {
      if (call.id == _pendingCallId) {
        _clearPendingCallData();
      }
    });
  }

  void _bindActiveCall(Call call) {
    setState(() {
      _activeCall = call;
    });
    // 绑定成功后可执行接听等操作
  }

  void _clearPendingCallData() {
    _prefs.remove('pending_call_id');
    _prefs.remove('pending_remote_number');
    setState(() {
      _pendingCallId = null;
      _remoteNumber = null;
      _activeCall = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('通话')),
      body: Center(
        child: _activeCall == null
            ? Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('正在连接: $_remoteNumber'),
                  const SizedBox(height: 20),
                  const CircularProgressIndicator(),
                ],
              )
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('通话对象: ${_activeCall?.remoteIdentity?.uri.user}'),
                  const SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: () => _activeCall?.answer(),
                    child: const Text('接听'),
                  ),
                  const SizedBox(height: 10),
                  ElevatedButton(
                    onPressed: () {
                      _activeCall?.hangup();
                      _clearPendingCallData();
                    },
                    style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                    child: const Text('挂断'),
                  ),
                ],
              ),
      ),
    );
  }
}

额外优化:缩短SIP重连时间

默认的自动重连间隔可能较长,你可以主动调用sipUAHelper.register()触发即时注册,或者在初始化SIPUAHelper时设置更短的重连超时参数,进一步减少等待时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 13:31:07