应用程序处于后台时,在中断后恢复执行代码

时间:2012-05-02 02:16:52

标签: objective-c ios xcode avaudioplayer avaudiosession

我花了几天时间研究SO和其他网站以获得答案,但没有任何运气。

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

此外,我设法通过实现AVAudioSessionDelegate方法beginInterruption和endInterruptionWithFlags来处理电话,系统计时器或闹钟的中断。它适用于后台和前台模式,但最奇怪的事情发生在:

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

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

以下是.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方法中设置一些内容?

非常感谢你!

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

不要回避这个问题,但是在背景中发挥空洞的声音是一种黑客攻击。提供在后台播放声音的功能,以便您可以播放音乐或其他声音,以保证用户的利益。

如果您想在特定时间播放声音或以其他方式提醒用户,请考虑使用using local notifications

  

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

如果我没记错的话,当您的应用进入后台时,计时器会被暂停或取消。文档明确指出,当您的应用被中断时,即当它被发送到后台时,您应该"stop timers and other periodic tasks"