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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 11:15:33