4

我正在开发基于 VoIP 的 IOS 应用程序。

有两种方法可以在来电时播放一些声音以通知用户:

  1. UILocalNotification用声音发送。声音最多持续30秒;
  2. setKeepAliveTimeout:handler:在函数中播放本地音乐资产。但是系统只给了我 10 秒的时间来完成操作。

有没有办法像原生的电话应用一样永远播放声音?

4

2 回答 2

1

Islam Q. 的答案的问题是 AVAudioPlayer 不适用于通知,因为当您不使用该应用程序时,该应用程序将被静音。只有 UILocalNotification 可以让您的应用在不活动时发出声音。30 秒的限制是真实的。应该通过另一个 30 秒通知重复通知来完成。我不知道其他 VOIP 应用程序会持续响多久,但即使使用传统电话,通常也会有限制,比如一分钟。

我的策略是:

  • 在有人尝试联系时发送带有 29 秒警报的初始通知
  • 在您即将错过来电 40 秒后发送带有不同 29 秒警报的第二个通知
  • 100 秒后发送第三次未接电话通知

这将允许用户在 1:40 的时间内响应呼叫。

您的代码将如下所示:

UILocalNotification* notification = [[UILocalNotification alloc] init];
notification.alertBody = NSLocalizedString(@"FIRST_CALL", @"%@ is calling you!");
notification.soundName = @"normal_ring.mp3";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(40 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    if (!phone.isRinging)
    {
        // Somebody picked it up already in the mean time
        return;
    }    

    UILocalNotification* notification = [[UILocalNotification alloc] init];
    notification.alertBody = NSLocalizedString(@"HURRY_CALL", @"Hurry, you are about to miss a call from %@!");
    notification.soundName = @"hurry_ring.mp3";
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        if (!phone.isRinging)
        {
            // Somebody picked it up already in the mean time
            return;
        }  

        UILocalNotification* notification = [[UILocalNotification alloc] init];
        notification.alertBody = NSLocalizedString(@"MISSED_CALL", @"You've just missed a call from %@");
        notification.soundName = @"missed_call.mp3";
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

    });
});

如果您使用代码,请记住,您首先需要确保应用程序在后台,因此 UILocalNotification 将是您拥有的唯一选项。在前台使用普通的 AVAudioPlayer,更加灵活。第二件事是当用户响应呼叫时应用程序变为前台,因此它应该静音警报并让 AVAudioPlayer 接管。

您希望在警报和应用内声音之间或多或少平滑过渡,以便为您的用户提供完美的体验,因此最好的办法是测量响铃之间的时间并让 AVAudioPlayer 在正确的时间启动新循环的开始。

您的第二个问题是让应用程序在后台保持活动状态。这个简单的代码将简单地不时地轮询应用程序以使其保持活动状态。它直接来自执行此操作的工作应用程序,此代码轮询计时器是否仍在运行并在运行时休眠五秒钟:

UIBackgroundTaskIdentifier backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
}];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [timerExecuted timerUpdate];

    while (!timerExecuted.isStopped)
    {
        [NSThread sleepForTimeInterval:5];
    }

    [application endBackgroundTask:backgroundTask];
});
于 2014-06-07T15:47:09.307 回答
1

恐怕@Dan2552 是正确的。

这是苹果公司所说的:

不支持持续时间超过 30 秒的声音。如果您指定的文件的声音播放时间超过 30 秒,则会播放默认声音。


编辑:

播放音频文件超过 30 秒(或永远,为此)可以通过使用AVAudioPlayerfromAVFoundation

@import AVFoundation;  // module -> no need to link the framework
// #import <AVFoundation/AVFoundation.h> // old style

- (void)playAudio
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"];
    NSError *error = nil;
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
    if (!error) {
        player.numberOfLoops = -1; // infinite loop
        [player play];
    } else {
        NSLog(@"Audio player init error: %@", error.localizedDescription);
    }
}

然后,您必须在主线程上调用此方法,而不是设置本地通知的 soundName 属性:

[self performSelectorOnMainThread:@selector(playAudio) withObject:nil waitUntilDone:NO];
于 2014-03-27T17:43:26.520 回答