如何在应用程序被挂起时使用CTCallCenter:setCallEventHandler:获取发生的呼叫事件?

12

CTCallCenter:setCallEventHandler:的文档指出:

然而,当你的应用程序处于挂起状态时,呼叫事件也可能发生。当它被挂起时,你的应用程序不会收到呼叫事件。当你的应用程序恢复活动状态时,它会接收到每个更改状态的呼叫的单个呼叫事件。

与此问题相关的部分是:

当您的应用程序恢复活动状态时,它会接收到每个更改状态的呼叫的单个呼叫事件

这意味着应用程序将会接收到在应用程序挂起期间发生的通话事件。根据此问题的答案,这是可能的:How does the Navita TEM app get call log information?

我的问题是:如果我的应用程序被挂起而且有一通电话,那么当我的应用程序恢复活动状态时,如何检索发生的呼叫事件?

我已经尝试了许多代码实验,但是在应用程序恢复活动状态时无法检索任何呼叫信息。

以下是我尝试的最简单的内容:

1)使用Xcode单视图应用程序模板创建一个新项目。

2)将下面的代码添加到didFinishLaunchingWithOptions

3)启动应用程序

4)离开应用程序

5)从另一设备拨打电话,接听电话,从任一设备挂断电话

6)将应用程序带回前台,以恢复活动状态。

注册呼叫事件的代码是:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
        self.callCenter = [[CTCallCenter alloc] init];
        [self.callCenter setCallEventHandler:^(CTCall *call)
         {
             NSLog(@"Event handler called");
             if ([call.callState isEqualToString: CTCallStateConnected])
             {
                 NSLog(@"Connected");
             }
             else if ([call.callState isEqualToString: CTCallStateDialing])
             {
                 NSLog(@"Dialing");
             }
             else if ([call.callState isEqualToString: CTCallStateDisconnected])
             {
                 NSLog(@"Disconnected");

             } else if ([call.callState isEqualToString: CTCallStateIncoming])
             {
                 NSLog(@"Incomming");
             }
         }];  

    return YES;
}

使用这段代码,如果应用程序在呼叫发生时处于前台,则可以获取呼叫事件。但是,如果在进行呼叫之前将任务从应用程序中删除,则无法在下一次恢复活动状态时获取呼叫事件-正如苹果文档中所述。

我尝试过的其他事情:

1)文档指出,块对象在默认优先级全局调度队列上分派,因此我尝试在dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{})中放置setCallEventHandler的注册。

2)在appBecameActive而不是didFinishLaunchingWithOptions中调用setCallEventHandler:

3)通过beginBackgroundTaskWithExpirationHandler和/或使用startUpdatingLocation或startMonitoringForSignificantLocationChanges添加背景功能到应用程序中。

4)以上各种组合。

只要我在设备上获得能够获取在应用挂起期间发生的呼叫事件的代码,就会授予奖励。

这是在iOS 7上。


在didFinishLaunchingWithOptions函数的第一行添加一个日志,并再次运行其中一个失败的测试,日志是否记录? - Wain
是的,我对使用处理程序的基本设置没有问题 - 如果应用程序在前台中,我可以获取更新,如我在2)中提到的那样。同时,在后台中,直到过期处理程序到期之前,我也可以获取它们。因此,它在didFinishLaunchingWithOptions中被注册并被调用。 - Gruntcakes
我其实在想,回调函数是否被某种方式替换并启动了新会话,导致事件列表丢失了... - Wain
1
我刚刚注意到另外一件事情 - 如果应用程序在前台时,我从另一个设备拨打电话,那么应用程序会在转到后台之前接收到来电和连接事件。通话结束后,应用程序再次变为活动状态,但它从未收到断开连接的事件。它不应该收到吗? - Gruntcakes
它肯定应该有。我之前只跟踪过通话,同时也跟踪位置,并且是从单例控制器(而不是应用程序委托)中进行的。我没有看到类似于你的情况。 - Wain
显示剩余3条评论
2个回答

15

我已经找到了一个解决方案,但是我不知道为什么它有效。我能想到的唯一事情就是GCD和/或CoreTelephony中的错误。

基本上,我像这样分配了两个CTCallCenter实例:

void (^block)(CTCall*) = ^(CTCall* call) { NSLog(@"%@", call.callState); };

-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    callCenter1 = [[CTCallCenter alloc] init];
    callCenter1.callEventHandler = block;

    callCenter2 = [[CTCallCenter alloc] init];
    callCenter2.callEventHandler = block;

    return YES;
}

Swift中类似的代码:

func block (call:CTCall!) {
        println(call.callState)
    }

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        //Declare callcenter in the class like 'var callcenter = CTCallCenter()'
        callcenter.callEventHandler = block

        return true
    }

为了测试这个,我打了一个电话,在应用程序在后台时接听了它,然后挂断了它。当我启动应用程序后,我收到了3个电话事件: 来电、已连接、已断开连接。


2
谢谢。由于您提供了一个可行的解决方案,我会给您奖励,但是我想知道是否有“正确”的解决方案。由于苹果在他们的文档中说明了这种行为,因此可能还有另一种方法来实现这一点,我们希望如此。如果上述问题是由于错误导致的,那么依赖它是很脆弱的。 - Gruntcakes
好的,文档明确说明它并不完整或最终。至少当我使用Xcode 5查看时是这样的。因此,依赖文档也没有什么用处。 - creker
大家好,这对我不起作用。启动应用程序并转到主屏幕后,我会打电话给自己,接听并挂断电话,但仍然看不到任何日志记录。我错过了什么吗? - Obj-Swift
1
这在应用程序处于前台模式下非常有效,但是当应用程序处于“后台模式”时无效。请问有什么帮助吗? - Vishal Sharma
对我来说似乎有效的方法(有时也在后台模式下)是按照答案中提到的分配2个CTCallCenter,但仅使用一个用于事件处理程序,另一个用于currentCalls属性。这样看来,currentCalls属性是一致的,有时也可以使用事件处理程序(并非总是)。根据http://stackoverflow.com/questions/26404449/ctcallcenter-doesnt-update-the-currentcalls-property-after-i-set-calleventhandl。 - Hooloovoo
显示剩余3条评论

3
在我的情况下,我正在开发一个不需要通过苹果应用市场批准的企业应用 - 因此,如果您正在开发企业应用,则此解决方案适用于您。 同时,所选择的答案在应用程序处于后台时无法工作。 解决方案很简单,基本上您只需要在功能选项卡中添加2个功能(VOIP和后台获取):
  • 您的项目目标 -> 功能 -> 后台模式 -> 标记语音通话后台获取

enter image description here

现在,你的应用程序已注册到名为“delegate”的iOS框架调用中,因此OP代码片段解决方案如下:
[self.callCenter setCallEventHandler:^(CTCall *call)
     {
         NSLog(@"Event handler called");
         if ([call.callState isEqualToString: CTCallStateConnected])
         {
             NSLog(@"Connected");
         }
         else if ([call.callState isEqualToString: CTCallStateDialing])
         {
             NSLog(@"Dialing");
         }
         else if ([call.callState isEqualToString: CTCallStateDisconnected])
         {
             NSLog(@"Disconnected");

         } else if ([call.callState isEqualToString: CTCallStateIncoming])
         {
             NSLog(@"Incomming");
         }
     }];  

一定能够正常工作,即使您的应用程序在后台也会收到通知。

这并不是一个简单的解决方案。你不能只是添加VoIP功能 - 除非它有合理的使用VoIP的原因,否则苹果将会拒绝在应用商店中发布该应用,即它必须是一款向用户呈现VoIP功能的应用程序。仅仅像这样添加VoIP功能将会导致你的应用被拒绝。 - Gruntcakes
@Woofbeans 在我的情况下,我正在开发一个企业应用程序,不需要苹果的应用商店。我将把“简单解决方案”短语更改为相关的短语。由于这个解决方案可以帮助您开发不需要苹果应用商店的应用程序。 - OhadM
1
嘿,不好意思打扰了一个旧的主题。我尝试了你提供的方法,但当应用程序在后台运行时,我仍然无法让任何代码起作用。我启动应用程序,然后按Home键,然后打电话,没有任何反应。但是在此之后,如果我运行我的应用程序,那么我就可以获得通话信息。在应用程序保持后台状态的同时,我能否获取有关通话的信息? - NecipAllef
1
在我参与那个项目的时候,这个应用程序实际上在后台执行了某些操作。如果应用程序在后台执行某些操作(当应用处于 BG 时状态栏上方会显示红条),你将收到回调。如果应用程序不会在后台执行任何操作,则在下一次应用程序进入前台时你会得到回调(如果我没有记错的话)。很抱歉,但这就是我对这个项目的记忆。你可以尝试检查一下... - OhadM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接