iOS – AVAudioPlayer在后台不会继续播放下一首歌曲

所以我有一个应用程序,播放一堆歌曲,而用户可以翻阅一本漫画书。 我使用AVAudioPlayer,并设置了播放歌曲的顺序。 所以当一首歌完成后,下一首将会播放。 这应用程序打开时,完美的工作。 当应用程序在后台时发生问题。 我设置了应用程序在后台播放,而且工作正常。 所以当用户按下主屏幕时,音乐继续播放。 问题发生在歌曲结束时,应该打开应用程序时播放下一首歌曲。 相反,没有发生。 根据我的NSLog语句正确的方法被调用,但没有任何反应。 这是我的代码:

- (void)audioPlayerDidFinishPlaying: (AVAudioPlayer *)player successfully: (BOOL) flag { NSLog(@"Song finished"); if ([songSelect isEqualToString: @"01icecapades"]) { isPlay = @"yes"; songSelect = @"02sugarcube"; imageSelect = @"playbanner02"; [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; } else if ([songSelect isEqualToString: @"02sugarcube"]) { isPlay = @"yes"; songSelect = @"03bullets"; imageSelect = @"playbanner03"; [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; } else if ([songSelect isEqualToString: @"03bullets"]) { isPlay = @"yes"; songSelect = @"04satanama"; imageSelect = @"playbanner04"; [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; } else if ([songSelect isEqualToString: @"04satanama"]) { isPlay = @"yes"; songSelect = @"05uglyjoke"; imageSelect = @"playbanner05"; [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; } else if ([songSelect isEqualToString: @"05uglyjoke"]) { isPlay = @"yes"; songSelect = @"01icecapades"; imageSelect = @"playbanner01"; [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; }} 

上面是识别哪首歌正在播放的代码,然后设置正确的歌曲。 然后触发另一种设置玩家的方法。

 - (void)triggerSong { NSLog(@"triggerSong called"); NSString *path; NSError *error; // Path the audio file path = [[NSBundle mainBundle] pathForResource:songSelect ofType:@"mp3"]; // If we can access the file... if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { // Setup the player player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error]; //player = [initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error]; [player setDelegate: self]; // Set the volume (range is 0 to 1) player.volume = 1.0f; [player prepareToPlay]; [player setNumberOfLoops:0]; [player play]; NSLog(@"player play"); [error release]; player.delegate = self; // schedules an action every second for countdown [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTimeLeft) userInfo:nil repeats:YES]; }} 

现在我假设这不是最好的方式来做到这一点,但是当应用程序处于前台状态时,它工作得很好。 我一直在查看文档,我似乎无法find这个问题的原因。 我希望有人能够看到我的方法错误。 就像我之前所说的,triggerSong方法中的两个NSLogs被调用,所以我不明白为什么AVAudioPlayer(播放器)没有被调用。

此外,我有我的info.plist中正确的设置,我有我的viewDidLoad:

 //Make sure the system follows our playback status [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [[AVAudioSession sharedInstance] setActive: YES error: nil]; 

感谢您的任何见解。 非常感激。

有关讨论

简答题:

您需要在第一个视图控制器的init或viewDidLoad方法中使用以下代码:

 [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 

长答案W /样品:

这是我的例子。 和你一样,我从一个可以在后台播放音乐的应用程序开始,但在第一个剪辑结束之后无法继续播放。 我做了一个原始Music.mp3的副本,并命名为Music2.mp3。 我的意图是一Music.mp3结束(audioPlayerDidFinishPlaying 🙂 Music.mp3。 我把这些背景任务搞糊涂了一段时间,直到我在没有后台任务的情况下才开始工作:

 -(id)init{ self = [super initWithNibName:@"MediaPlayerViewController" bundle:nil]; if(self){ //Need this to play background playlist [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; //MUSIC CLIP //Sets up the first song... NSString *musicPath = [[NSBundle mainBundle] pathForResource:@"Music" ofType:@"mp3"]; if(musicPath){ NSURL *musicURL = [NSURL fileURLWithPath:musicPath]; audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil]; [audioPlayer setDelegate:self]; } } return self; } -(IBAction)playAudioFile:(id)sender{ if([audioPlayer isPlaying]){ //Stop playing audio and change test of button [audioPlayer stop]; [sender setTitle:@"Play Audio File" forState:UIControlStateNormal]; } else{ //Start playing audio and change text of button so //user can tap to stop playback [audioPlayer play]; [sender setTitle:@"Stop Audio File" forState:UIControlStateNormal]; } } -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ [audioButton setTitle:@"Play Audio File" forState:UIControlStateNormal]; [playRecordingButton setTitle:@"Play Rec File" forState:UIControlStateNormal]; //PLAY THE SECOND SONG NSString *musicPath2 = [[NSBundle mainBundle] pathForResource:@"Music2" ofType:@"mp3"]; if(musicPath2){ NSURL *musicURL2 = [NSURL fileURLWithPath:musicPath2]; audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL2 error:nil]; [audioPlayer setDelegate:self]; NSLog(@"Play it again: \n%@", musicPath2); [audioPlayer play]; } } 

最终的结果是,我的应用程序正在连续循环播放Music2.mp3,即使应用程序在后台。

只是为了确认Squatch说什么,这也是Swift的解决scheme:

 UIApplication.sharedApplication().beginReceivingRemoteControlEvents() 

OS X展示使用AVAudioPlayer相同的问题,但UIApplication是一个iOS的唯一构造。 OS X需要使用NSApplication,但NSApplication不会返回,直到应用程序终止,因此我们需要使用线程。 作为奖励,在NSApplication的深处有一个assert()需要主线程。

这个混合的C ++ / Objective C函数是这个OS X问题的一个解决方法:

 void do_the_dumb (void real_application(void)) { std::thread thread ([real_application]() { real_application(); [[NSApplication sharedApplication] terminate: [NSApplication sharedApplication]]; }); [[NSApplication sharedApplication] run]; thread.join(); };