iOS终止状态下通过VOIP通知接入Agora通话的实现方案
解决iOS终止状态下VOIP来电接入Agora通话的方案
核心思路
App终止时收到VOIP推送,系统会唤醒App到后台,但Flutter引擎未初始化。需要先在原生侧启动Flutter引擎,再通过MethodChannel触发Dart侧的Agora通话逻辑,同时配合CallKit的接听事件完成通话接入。
具体实现步骤
1. 在PushKit回调中初始化Flutter引擎
修改AppDelegate.swift中的pushRegistry(_:didReceiveIncomingPushWith:for:completion:)方法,在展示CallKit前后初始化Flutter引擎,确保Dart环境可用:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { print("didReceiveIncomingPushWith") guard type == .voIP else { return } let id = payload.dictionaryPayload["id"] as? String ?? "" let nameCaller = payload.dictionaryPayload["nameCaller"] as? String ?? "" let handle = payload.dictionaryPayload["handle"] as? String ?? "" let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false let extra = payload.dictionaryPayload["extra"] as? NSDictionary ?? [:] // 初始化Flutter引擎(仅当未初始化时执行) if FlutterEngineGroup.sharedEngineGroup().engines.isEmpty { let engine = FlutterEngine(name: "VOIPCallEngine") engine.run() GeneratedPluginRegistrant.register(with: engine) } // 传递通话参数到Dart侧 let channel = FlutterMethodChannel(name: "com.your.app/voip_call", binaryMessenger: FlutterEngineGroup.sharedEngineGroup().engines.first!.binaryMessenger) channel.invokeMethod("setupIncomingCall", arguments: [ "id": id, "nameCaller": nameCaller, "handle": handle, "isVideo": isVideo, "extra": extra ]) // 展示CallKit来电界面 let data = flutter_callkit_incoming.Data(id: id, nameCaller: nameCaller, handle: handle, type: isVideo ? 1 : 0) data.extra = extra print("Notification: ", "INCOMING") SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true) completion() }
2. Dart侧监听MethodChannel并初始化Agora
在Dart代码中创建MethodChannel,监听原生传来的通话参数,提前初始化Agora引擎并准备加入频道:
import 'package:flutter/services.dart'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; class VoipCallHandler { static const MethodChannel _channel = MethodChannel('com.your.app/voip_call'); static String? _currentChannelId; static String? _currentToken; static Future<void> init() async { _channel.setMethodCallHandler((call) async { if (call.method == 'setupIncomingCall') { // 从推送参数中提取Agora所需的频道ID、Token等(需在payload的extra中携带) final extra = call.arguments['extra'] as Map; _currentChannelId = extra['channelId'] as String; _currentToken = extra['token'] as String; // 提前初始化Agora引擎 await AgoraRtcEngine.create(AgoraRtcEngineConfig(appId: 'YOUR_AGORA_APP_ID')); await AgoraRtcEngine.enableAudio(); } }); } // 用户接听CallKit时调用,加入Agora频道 static Future<void> joinCall() async { if (_currentChannelId != null && _currentToken != null) { await AgoraRtcEngine.joinChannel( token: _currentToken!, channelId: _currentChannelId!, uid: 0, // 根据业务逻辑设置UID options: ChannelMediaOptions() ); } } }
3. 处理CallKit接听事件
在flutter_callkit_incoming的事件监听中,触发Agora频道加入逻辑:
FlutterCallkitIncoming.onEvent.listen((event) { switch (event!.event) { case EventAction.ACTION_CALL_ACCEPT: // 用户接听电话,执行Agora频道加入 await VoipCallHandler.joinCall(); break; // 其他事件(如拒接、结束)的处理逻辑 default: break; } });
4. 关键注意事项
- 推送payload的
extra字段必须携带Agora通话所需的频道ID、Token、UID等核心参数。 - 原生侧初始化Flutter引擎时要避免重复创建,通过判断引擎组是否为空来控制。
- 需在
Info.plist中开启Voice over IP和Remote notifications后台权限。 - Agora引擎初始化要尽量轻量化,避免后台占用过多资源被系统回收。
内容的提问来源于stack exchange,提问作者ndev
相关产品推荐
相关产品推荐

