我花了几天时间在 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 方法中设置一些内容吗?
预先非常感谢您!