公司的app里面有后台音频播放功能,最近发现,当在锁屏界面(后台)时,不能控制播放页面的音频播放,是因为没有实现远程控制事件处理。
下面说说大概实现过程:由于后台播放功能早已实现(不做详细叙述),这里主要记录一下远程控制的实现(方便以后复习)。
1、在文件APPDelegate.m里,实现方法“- (void)remoteControlReceivedWithEvent:(UIEvent *)event”的重写,用来接收控制中心的事件。
// 在APPDelegate.m中声明一个通知事件的key
NSString *const AppDelegateReceiveRemoteEventsNotification = @"AppDelegateReceiveRemoteEventsNotification";
/// 锁屏页面的控制事件(必须在这里重写该方法,在播放页面重写不起作用)
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
if (event.type == UIEventTypeRemoteControl) {
// 发送通知给音频播放界面 进行某些处理
[[NSNotificationCenter defaultCenter] postNotificationName:AppDelegateReceiveRemoteEventsNotification object:event];
}
}
Appdelegate里就设置这些。
2、在音频播放界面
// 设置控制中心交互需要导入
#import
/// 注册通知
- (void) registerAllNotifications
{
// 后台通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
// 进入前台通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(apllicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
// 锁屏界面事件
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AppDelegateReceiveRemoteEventsNotification:) name:AppDelegateReceiveRemoteEventsNotification object:nil];
}
#pragma mark - 通知方法实现
/// 进入后台
- (void) apllicationWillResignActiveNotification:(NSNotification *)n
{
NSError *error = nil;
// 后台播放代码
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:&error];
if(error) {
NSLog(@"ListenPlayView background error0: %@", error.description);
}
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if(error) {
NSLog(@"ListenPlayView background error1: %@", error.description);
}
/// 进后台 设置接收远程控制
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// 上面代码实现后台播放 几分钟后会停止播放
// 添加任务id 实现后台连续播放
backTaskID = [self backgroundPlayerID:backTaskID];
}
// 实现一下backgroundPlayerID:这个方法:
- (UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
{
NSError *error = nil;
// 设置并激活音频会话类别
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
if(error) {
NSLog(@"ListenPlayView background error2: %@", error.description);
}
[session setActive:YES error:nil];
if(error) {
NSLog(@"ListenPlayView background error3: %@", error.description);
}
// 设置后台任务ID
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
if(newTaskId != UIBackgroundTaskInvalid && backTaskId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:backTaskId];
}
return newTaskId;
}
/// 进入前台通知
- (void) apllicationWillEnterForegroundNotification:(NSNotification *)n {
// 进前台 设置不接受远程控制
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
/// 远程控制事件通知
- (void)AppDelegateReceiveRemoteEventsNotification:(NSNotification*)noti
{
if (!noti) return;
if (!noti.object) return;
if (![noti.object isKindOfClass:[UIEvent class]]) return;
UIEvent *event = (UIEvent *)noti.object;
if (event.type == UIEventTypeRemoteControl) {
// 根据远程控制事件类型,在播放界面设置播放、暂停、下一个、上一个
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
// play
break;
case UIEventSubtypeRemoteControlPause:
// pause
break;
case UIEventSubtypeRemoteControlNextTrack:
// next
break;
case UIEventSubtypeRemoteControlPreviousTrack:
// last
break;
default: break;
}
}
}
/// 移除所有通知
- (void)removeAllNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AppDelegateReceiveRemoteEventsNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
}
上面这些实现了控制中心和播放界面的交互。此时,控制中心还没有显示当前播放的音频信息(嗯,我还没有设置)。如下:
/// 设置锁屏界面的播放音频的相关信息
/// 可在适当时机调用该方法(开始播放时、将要进入后台、音频切换)
- (void) setupNowPlayingAudioInfo
{
/** 设置信息的类型
* MPMediaItemPropertyAlbumTitle : 专辑名
* MPMediaItemPropertyAlbumTrackCount : 专辑个数
* MPMediaItemPropertyAlbumTrackNumber : 当前播放的专辑位置
* MPMediaItemPropertyArtist : 艺术家
* MPMediaItemPropertyArtwork : 封面
* MPMediaItemPropertyComposer : 作曲家
* MPMediaItemPropertyDiscCount : 迪斯科 数量
* MPMediaItemPropertyDiscNumber : 当前位置
* MPMediaItemPropertyGenre : 流派
* MPMediaItemPropertyPersistentID : ID
* MPMediaItemPropertyPlaybackDuration : 后台播放时长
* MPMediaItemPropertyTitle : 标题
*/
NSMutableDictionary *songDict = [NSMutableDictionary dictionary];
// 音频名字
[songDict setObject:@"她来听我的演唱会" forKey:MPMediaItemPropertyTitle];
// 歌手
[songDict setObject:@"张学友" forKey:MPMediaItemPropertyArtist];
// 歌曲的总时间
[songDict setObject:@(200) forKeyedSubscript:MPMediaItemPropertyPlaybackDuration];
// 当前时间
[songDict setObject:@(30) forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
// 播放速率
[songDict setObject:@(1.0) forKey:MPNowPlayingInfoPropertyPlaybackRate];
// 锁屏音频封面
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"封面.png"]];
[songDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
// 设置控制中心歌曲信息
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songDict];
}
这时,锁屏界面已经可以显示音频对应的信息了。
还有最后一步设置(动态设置控制中心的按钮状态),如:当前播放的音频是播放列表最后一条,设置控制中心的下一个按钮不可点击。。。
/// 设置控制中心的按钮状态(保持与播放界面对应按钮状态一致)
/// 调用时机(如切换音频时,设置锁屏界面音频信息时)
- (void) setupRemoteControlButtonStatus
{
// 只有设置了target 才能设置控制中心(锁屏界面)的按钮状态
// 设置控制中心下一个按钮 和播放页面的下一个按钮状态同步
MPRemoteCommand *next = [MPRemoteCommandCenter sharedCommandCenter].nextTrackCommand;
next.enabled = self.nextButton.enabled;
[next addTarget:self action:@selector(remoteCommandAction:)];
// 设置控制中心上一个按钮 和播放页面的上一个按钮状态同步
MPRemoteCommand *last = [MPRemoteCommandCenter sharedCommandCenter].previousTrackCommand;
last.enabled = self.theLastButton.enabled;
[last addTarget:self action:@selector(remoteCommandAction:)];
// 设置控制中心播放|暂停按钮 和 播放页面的播放|暂停按钮状态同步
MPRemoteCommand *play = [MPRemoteCommandCenter sharedCommandCenter].playCommand;
play.enabled = self.playButton.enabled;
[play addTarget:self action:@selector(remoteCommandAction:)];
MPRemoteCommand *pause = [MPRemoteCommandCenter sharedCommandCenter].pauseCommand;
pause.enabled = self.playButton.enabled;
[pause addTarget:self action:@selector(remoteCommandAction:)];
}
/// 实现target action
- (void)remoteCommandAction:(MPRemoteCommand *)com
{
// 这里不用具体做些什么 do nothing..
// 控制中心的事件已经在通知AppDelegateReceiveRemoteEventsNotification里实现了
}
设置完毕(这些设置已经足够公司app的需要了,有需要再补充)。
参考了一下其他博客,如https://www.cnblogs.com/gchlcc/p/5582571.html