iOS 设置后台定时闹铃

一.什么是AVAudioSession ?
我的理解就是一个应用最基本的音频配置,来设置基本的应用与系统之间的音频处理方式。

1). Audio Session 各个参数类别的作用及定义:
https://developer.apple.com/library/prerelease/tvos/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/ConfiguringanAudioSession/ConfiguringanAudioSession.html

2).配置音频后台播放及官方资料:
https://developer.apple.com/library/ios/qa/qa1668/_index.html

二.AVAudioSession在开发中的应用

1)应用里的音频播放时是否要和其他应用的音频实现混音?或者让其他音频静音?
2)当iOS的闹钟响时应用内的音频是否要暂停?
3)当用户插拔耳机时应用应该如何反应?拔出耳机时是否要停止音乐?
4)关闭屏幕后音频是否暂停?

三.后台定时闹钟的实现

1.实现方式:

1).设置后台无限运行
方式:当程序退回后台时,系统会允许应用5分钟的存活时间。在这段时间里,启动一个定时器,每间隔一两分钟就执行播放一段非常短的无声音频,来继续获取系统重新的5分钟的存活时间,达到在后台无限运行的效果。

后台无线运行Demo例子: https://github.com/mddios/runInBackground

2).设置定时闹钟
方式:使用本地通知 UILocalNotification + 播放器AVAudioPlayer

3).闹钟响起时
方式:弹出本地通知及播放指定歌曲

2.遇到的问题:

问题1:
在设置定时闹钟后,退回后台;打开第三方播放器播(例如酷狗音乐)播放歌曲,当定时闹钟时间到后,弹出本地通知,但是设置的播放闹铃歌曲并没有播放出来。经调试发现,此时在后台中执行setActive失败:

    BOOL activated = [[AVAudioSession sharedInstance] setActive:YES error:&error];
    if(activated)
    {
      NSLog(@"OK");
    }else {
      NSLog(@"error: %@",error);
    }

解决办法:
在定时闹钟时间到后,设置AVAudioSession参数为允许其他播放器混音选项AVAudioSessionCategoryOptionMixWithOthers;这样就能解决后台播放失败的问题。

  // 设置接受远程控制事件
  [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 
  
  AVAudioSession *session = [AVAudioSession sharedInstance];

  //设置允许混音
  [session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
                
  [session setActive:YES error:nil];
                
  //播放歌曲
  _play = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
  [_play play];

问题2:
设置了混音AVAudioSessionCategoryOptionMixWithOthers后,虽然能决解了后台定时闹钟响铃失败的问题,但是若此时打开自己APP的播放器播放歌曲,会发现自己的APP无法暂停其他APP播放器(如酷狗播放器)的音乐;别的APP播放器也暂停不了自己APP的音乐。
调试发现,之前设置的接收中断通知服务失效:


//监听电话进来 或 打开其他APP音乐播放歌曲时接收的中断信息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];
    
// 中断处理
-(void)onAudioSessionEvent:(NSNotification*)noty{
    NSLog(@"%@",noty.userInfo);

    NSDictionary *dict = noty.userInfo;
    if ([dict[AVAudioSessionInterruptionTypeKey]integerValue]==AVAudioSessionInterruptionTypeBegan) {
        //正当音乐被其他应用或者电话中断时
    }else if([dict[AVAudioSessionInterruptionTypeKey]integerValue]==AVAudioSessionInterruptionTypeEnded){
       //当中断结束时
    }
}

解决办法:
在应用进入前台时,设置AVAudioSession的参数为AVAudioSessionCategorySoloAmbient选项,即关闭其他APP应用播放器音乐;但是此选项不支持后台音乐播放,故需要再次设置AVAudioSession的参数为AVAudioSessionCategoryPlayback,即允许后台播放歌曲。

// 应用进入前台
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    //允许应用程序接收远程控制
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

    AVAudioSession *session = [AVAudioSession sharedInstance];
    
    // 设置为暂停其他播放器音乐
    [session setCategory:AVAudioSessionCategorySoloAmbient error:nil];

    // 设置播放器为允许后台播放
    [session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
    [session setActive:YES error:nil];
}