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

Flutter应用终止时维持WebSocket连接监听WebRTC来电的方案咨询

如何在Flutter应用终止状态下保持WebSocket连接以监听WebRTC来电

核心问题分析

应用进入终止(detached)状态时,系统会回收进程资源,纯Flutter层面的后台服务无法持续存活。要实现来电监听,必须结合平台级后台能力和Asterisk端配置调整,仅靠普通后台重连逻辑无法解决根本问题。

可行解决方案

1. 平台级后台保活策略

Android端

  • 启用前台服务:Android 8.0+对后台服务限制严格,只有带通知的前台服务能避免被系统杀死。使用flutter_background_service时必须配置前台通知,将服务标记为活跃状态。
  • 申请必要权限:添加WAKE_LOCK权限防止设备休眠导致连接断开,同时引导用户开启应用的后台无限制运行、自启动权限(国内厂商定制系统需额外适配)。

iOS端

  • 使用VoIP推送(PushKit):iOS不允许后台持续运行WebSocket连接,只有VoIP推送能直接唤醒终止的应用。需要:
    • 在Xcode中配置PushKit,开启VoIP权限;
    • 将设备的VoIP推送令牌注册到Asterisk;
    • 当有来电时,Asterisk发送VoIP推送唤醒应用,应用重建WebSocket连接并处理来电。

2. 优化WebSocket重连逻辑

  • 增加定时重试机制:不要仅依赖transportStateChanged回调,每隔30秒(可按需调整)主动检查连接状态,断开则触发重连,同时实现指数退避(比如重连失败后延迟1分钟、2分钟再尝试)避免频繁请求。
  • 独立初始化SipHelper:应用终止后原实例已销毁,后台服务中需重新创建SipUaHelper实例,而非复用之前的对象。

修改后的后台服务代码示例:

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:sip_ua/sip_ua.dart';

@pragma('vm:entry-point')
static void onStart(ServiceInstance service) async {
  // Android: 配置前台服务通知
  if (Platform.isAndroid) {
    await service.setForegroundNotificationInfo(
      title: 'SIP来电监听',
      content: '后台保持连接',
    );
    await service.startForegroundService();
  }

  // 重新初始化SipHelper实例
  final sipHelper = SipUaHelper();
  final listener = SipUAListenerBackground(sipHelper);
  sipHelper.addSipUaHelperListener(listener);

  // 读取本地保存的登录配置
  final savedLoginInfo = await _loadLoginConfig(); // 自行实现本地存储读取逻辑
  if (savedLoginInfo == null) return;

  // 初始重连
  await _reconnectSip(sipHelper, savedLoginInfo);

  // 定时检查连接状态
  Timer.periodic(const Duration(seconds: 30), (timer) async {
    if (sipHelper.transportState != TransportState.CONNECTED) {
      await _reconnectSip(sipHelper, savedLoginInfo);
    }
  });
}

static Future<void> _reconnectSip(SipUaHelper sipHelper, Map<String, dynamic> config) async {
  try {
    // 先断开现有连接(如果存在)
    if (sipHelper.transportState == TransportState.CONNECTED) {
      await sipHelper.stop();
    }

    final settings = UaSettings()
      ..webSocketUrl = config['websocketUrl']
      ..uri = 'sip:${config['username']}@${config['server']}'
      ..authorizationUser = config['authorizationUser']
      ..password = config['password']
      ..displayName = config['displayName']
      ..userAgent = 'Flutter SIP Client'
      ..dtmfMode = DtmfMode.RFC2833;

    await sipHelper.start(settings);
    print('SIP连接重建成功');
  } catch (e) {
    print('SIP重连失败: $e');
  }
}

static Future<Map<String, dynamic>?> _loadLoginConfig() async {
  // 从SharedPreferences或其他本地存储读取配置
  // 示例实现:
  // final prefs = await SharedPreferences.getInstance();
  // return {
  //   'websocketUrl': prefs.getString('websocketUrl'),
  //   'username': prefs.getString('username'),
  //   ...
  // };
  return null;
}

class SipUAListenerBackground implements SipUaHelperListener {
  final SipUaHelper _sipHelper;

  SipUAListenerBackground(this._sipHelper);

  @override
  void transportStateChanged(TransportState state) {
    if (state == TransportState.CLOSED) {
      // 连接断开时触发重连(配合定时检查,双重保障)
      _reconnectSip(_sipHelper, await _loadLoginConfig());
    }
  }

  // 实现其他SipUaHelperListener方法(按需)
  @override
  void registrationStateChanged(RegistrationState state) {}
  @override
  void callStateChanged(Call call, CallState state) {}
  // ... 其他回调
}

3. Asterisk端配置调整

  • 开启SIP推送功能:在pjsip.conf中配置推送参数,针对iOS VoIP推送和Android FCM推送,当客户端WebSocket断开时,Asterisk发送推送通知唤醒应用。
  • 调整会话超时:延长Asterisk的来电等待时间,给客户端足够的重连时间,避免因连接未及时重建而挂断来电。

总结

无法在所有场景下完全保持WebSocket连接(尤其是iOS),但通过平台级后台能力+智能重连+Asterisk推送配置的组合方案,可最大化保证来电能被及时接收。Android依赖前台服务保活,iOS依赖VoIP推送唤醒,两者缺一不可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 02:47:33