4

我花了几天时间在 SO 和其他网站上研究这个问题的答案,但没有任何运气。

从本质上讲,我为自己设定的挑战是为 iOS 创建一个无论用户在哪里(前景或背景)都能发出声音的闹钟应用。我已经通过使用 AVAudioPlayer 实例并在用户设置警报时开始播放空声音文件以使应用程序继续在后台运行来实现这一点。当警报响起时(即当 NSTimer 被触发时),已经启动并准备播放的第二个播放器开始播放用户醒来的铃声。

此外,我还通过实现 AVAudioSessionDelegate 方法 beginInterruption 和 endInterruptionWithFlags 来处理电话、系统计时器或闹钟的中断。它可以在后台和前台模式下工作,但最奇怪的事情发生了:

当中断结束时,AVAudioPlayer 会继续播放,但除非我再次将应用程序置于前台,否则我无法在我的应用程序中执行任何其他代码。

为了深入了解这一点,我尝试了一个更简单的项目,我将在下面发布。这个应用程序所做的是,一旦您进入应用程序,AVAudioPlayer 类的一个实例就会开始循环播放某种声音。然后当你把它带到后台时,播放器会继续循环播放声音。当发生中断时,我暂停播放器,当它结束时,我使用调度等待几秒钟,然后它调用两个方法,即 (void)playPlayer,一个包含恢复播放文件的代码的方法和 (void)tester ,一种包含计时器的方法,该计时器设置为在中断结束后 5 秒(或准确地说是 7 秒)停止播放器。这两种方法都被调用,正如我在它们中放入的 NSLogs 所示,但计时器永远不会被触发,玩家会继续无限期地玩。

这是 .h 文件的代码:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

@interface InterruptionTest3ViewController : UIViewController <AVAudioSessionDelegate,      AVAudioPlayerDelegate>
{
    AVAudioSession *mySession;
    AVAudioPlayer *myPlayer;
}

-(void) playPlayer;
-(void) pausePlayer;
-(void) tester;

@end

这是 .m 文件的代码:

#import "InterruptionTest3ViewController.h"

@interface InterruptionTest3ViewController ()

@end

@implementation InterruptionTest3ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    mySession = [AVAudioSession sharedInstance];
    NSError *setActiveError = nil;
    [mySession setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:&setActiveError];
    if (setActiveError) {
        NSLog(@"Session failed to activate within viewDidLoad");
    }
    else {
        NSLog(@"Session was activated within viewDidLoad");
    }
    NSError *setCategoryError = nil;
    [mySession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
    if (setCategoryError) {
        NSLog(@"Category failed to be set");
    }
    else {
        NSLog(@"Category has been set");
    }

    [mySession setDelegate:self];


    NSString *path = [[NSBundle mainBundle] pathForResource:@"headspin" ofType:@"wav"];
    NSError *initMyPlayerError = nil;
    myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]     error:&initMyPlayerError];
    if (initMyPlayerError) {
        NSLog(@"myPlayer failed to initiate");
    }
    else {
        NSLog(@"myPlayer has been initiated");
    }

    [myPlayer prepareToPlay];
    [self playPlayer];

    OSStatus propertySetError = 0;
    UInt32 allowMixing = true;

    propertySetError = AudioSessionSetProperty (
                                                kAudioSessionProperty_OverrideCategoryMixWithOthers, 
                                                sizeof (allowMixing),                                 
                                                &allowMixing                                          
                                                );

    [myPlayer setNumberOfLoops:-1];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}

-(void) beginInterruption
{
    [myPlayer pause];
}

-(void) endInterruptionWithFlags:(NSUInteger)flags
{
    if (flags) {
        if (AVAudioSessionInterruptionFlags_ShouldResume) 
        {
            {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),dispatch_get_main_queue(), ^{

                    [self playPlayer];
                    [self tester];
                });
}
        }
    }
}

-(void) tester
{
    [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(pausePlayer) userInfo:nil repeats:NO];
    NSLog(@"tester method has been called");
}

-(void) playPlayer
{
    [NSTimer timerWithTimeInterval:5.0 target:myPlayer selector:@selector(stop) userInfo:nil repeats:NO];
    [myPlayer play];
    NSLog(@"playPlayer method has been called");
}

-(void) pausePlayer
{
    [myPlayer pause];
}



//viewDidUnload etc not listed.

所以,这就是伙计们。同样,为什么在应用程序处于后台时中断后计时器没有被触发?我需要在 applicationDidEnterBackground 方法中设置一些东西吗?

非常感谢您!

4

2 回答 2

0

请使用“[NSTimer scheduleTimerWithTimeInterval:5.0 target:myPlayer selector:@selector(stop) userInfo:nil repeats:NO]”;

于 2012-05-02T03:44:56.683 回答
0

不是为了回避这个问题,而是在后台播放空声音位是一种技巧。提供了在后台播放声音的功能,以便您可以播放音乐或其他声音以使用户受益,而不是保持后台进程处于活动状态。

如果您想在特定时间播放声音或以其他方式提醒用户,请考虑使用本地通知。

为什么在应用程序处于后台时中断后计时器没有被触发?

如果我没记错的话,当您的应用程序进入后台时,计时器会被暂停或取消。该文档明确指出,当您的应用程序被中断时,即当它被发送到后台时,您应该“停止计时器和其他周期性任务” 。

于 2012-05-02T03:54:32.427 回答