首页 > 代码库 > iOS音频的后台播放总结(后台网络请求歌曲,Remote控制,锁屏封面,各种打断)

iOS音频的后台播放总结(后台网络请求歌曲,Remote控制,锁屏封面,各种打断)

 

iOS音频的后台播放总结(后台网络请求歌曲,Remote控制,锁屏封面,各种打断) 

技术分享 分类:
 

目录(?)[+]

 

在没有网络的情况下,音频的后台播放比较简单,google一下可以搜到很多资料,但是如果每次歌曲的请求都是通过网络,就不成了,有时可以也扛不了几首,这里总结下实现方法,可以实现像电台一样的功能,后台播放,网络请求歌曲,Remote控制,锁屏有封面,电话和听歌打断处理等。

技术分享 技术分享

 

 

初始化AudioSession和基本配置

音频播放器采用的AVPlayer ,自己进行了功能封装,暂且不谈,在程序启动的时候需要配置AudioSession,AudioSession负责应用音频的设置,比如支不支持后台,打断等等,这一步很重要,比如在viewdidload里初始化AVplayer以后要调用下面的函数:

 

[objc] view plain copy
  1. -(void)setAudioSession{  
  2.   
  3. //AudioSessionInitialize用于控制打断 ,后面会说  
  4.   
  5. AudioSessionInitialize (  
  6.   
  7. NULL,                          // ‘NULL’ to use the default (main) run loop  
  8.   
  9. NULL,                          // ‘NULL’ to use the default run loop mode  
  10.   
  11. ASAudioSessionInterruptionListener,  // a reference to your interruption callback  
  12.   
  13. NULL                       // data to pass to your interruption listener callback  
  14.   
  15. );  
  16.   
  17. //这种方式后台,可以连续播放非网络请求歌曲,遇到网络请求歌曲就废,需要后台申请task  
  18.   
  19. AVAudioSession *session = [AVAudioSession sharedInstance];  
  20.   
  21. NSError *setCategoryError = nil;  
  22.   
  23. BOOL success = [session setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];  
  24.   
  25. if (!success)  
  26.   
  27. {  
  28.   
  29. /* handle the error condition */  
  30.   
  31. return;  
  32.   
  33. }  
  34.   
  35. NSError *activationError = nil;  
  36.   
  37. success = [session setActive:YES error:&activationError];  
  38.   
  39. if (!success)  
  40.   
  41. {  
  42.   
  43. /* handle the error condition */  
  44.   
  45. return;  
  46.   
  47. }  
  48.   
  49. }  


 

AudioSessionInitialize用于处理中断处理,AVAudioSession主要调用setCategory和setActive方法来进行设置,AVAudioSessionCategoryPlayback一般用于支持后台播放,在官方文档可以看到其他的类型,每个分别适用于不同的场合:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryAmbient">
<blockquote>AVAudioSessionCategoryAmbient</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategorySoloAmbient">
<blockquote>AVAudioSessionCategorySoloAmbient</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryPlayback">
<blockquote>AVAudioSessionCategoryPlayback</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryRecord">
<blockquote>AVAudioSessionCategoryRecord</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryPlayAndRecord">
<blockquote>AVAudioSessionCategoryPlayAndRecord</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryAudioProcessing">
<blockquote>AVAudioSessionCategoryAudioProcessing</blockquote>
</a>;
NSString *const <a href="file:///Users/lipengxuan/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS6.0.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html#//apple_ref/doc/c_ref/AVAudioSessionCategoryMultiRoute">
<blockquote>AVAudioSessionCategoryMultiRoute</blockquote>
</a>;

 

 
1
 

除了代码的初始化,很重要的一步是对info-plist的设置,让应用支持音频的后台播放

技术分享

库的引入包括:

AudioToolBox.framework

MediaPlayer.framework

CoreMedia.framework

AVFoundation.framework

 

后台播放

正常情况下,如果配置了AVAudioSessionCategoryPlayback这个方法并修改了info-plist文件,应用就已经支持后台音频播放了,但是如果每一首歌曲都不存在本地,在网络的话就不行了,需要申请后台任务来进行处理,首先修改:

 

[objc] view plain copy
  1. - (void)applicationDidEnterBackground:(UIApplication *)application {  
  2.   
  3. [application beginReceivingRemoteControlEvents];  
  4. }  


 

然后在播放器的播放函数里添加:

 

[objc] view plain copy
  1. -(void)justPlay{  
  2.   
  3. UIBackgroundTaskIdentifier bgTask = 0;  
  4.   
  5.    
  6.   
  7. if([UIApplication sharedApplication].applicationState== UIApplicationStateBackground) {  
  8.   
  9. NSLog(@”xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx后台播放”);  
  10.   
  11. [thePlayer play];  
  12.   
  13. UIApplication*app = [UIApplication sharedApplication];  
  14.   
  15. UIBackgroundTaskIdentifier newTask = [app beginBackgroundTaskWithExpirationHandler:nil];  
  16.   
  17. if(bgTask!= UIBackgroundTaskInvalid) {  
  18.   
  19. [app endBackgroundTask: bgTask];  
  20.   
  21. }  
  22.   
  23. bgTask = newTask;  
  24.   
  25. }  
  26.   
  27. else {  
  28.   
  29. NSLog(@”xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx前台播放”);  
  30.   
  31. [thePlayer play];  
  32.   
  33. }  
  34.   
  35. }  


 

这样播放就可以进行前台或者后台的判断,支持网络后台播放了,一首一首连续播放。

Remote控制

在播放视图的ViewController里加上这两个函数:

 

[objc] view plain copy
  1. - (void)viewDidAppear:(BOOL)animated {  
  2.   
  3. NSLog(@”viewDidAppear!!!”);  
  4.   
  5. [super viewDidAppear:animated];  
  6.   
  7. //Once the view has loaded then we can register to begin recieving controls and we can become the first responder  
  8.   
  9. [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];  
  10.   
  11. [self becomeFirstResponder];  
  12.   
  13. }  
  14.   
  15. - (void)viewWillDisappear:(BOOL)animated {  
  16.   
  17. NSLog(@”viewWillDisappear!!!”);  
  18.   
  19. [super viewWillDisappear:animated];  
  20.   
  21. //End recieving events  
  22.   
  23. [[UIApplication sharedApplication] endReceivingRemoteControlEvents];  
  24.   
  25. [self resignFirstResponder];  
  26.   
  27. }  


 

当然也可以同理放到delegate.m里面的进入后台和回到前台的函数中,否则的话,上面的代码只是允许当前视图的情况下进入后台可以Remote控制

 

然后添加下面的代码:

 

 

[objc] view plain copy
  1. -(void)remoteControlReceivedWithEvent:(UIEvent *)event{  
  2.   
  3. //if it is a remote control event handle it correctly  
  4.   
  5. if (event.type == UIEventTypeRemoteControl) {  
  6.   
  7. if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {  
  8.   
  9. [self playerTap];  
  10.   
  11. else if (event.subtype == UIEventSubtypeRemoteControlNextTrack){  
  12.   
  13. [self nextSongAuto];  
  14.   
  15. [self configNowPlayingInfoCenter];  
  16.   
  17. }  
  18.   
  19. }  
  20.   
  21. }  
  22.   
  23. //Make sure we can recieve remote control events  
  24.   
  25. - (BOOL)canBecomeFirstResponder {  
  26.   
  27. return YES;  
  28.   
  29. }  


 

锁屏封面

一般在每次切换歌曲或者更新信息的时候要调用这个方法

 

[objc] view plain copy
  1. - (void)configNowPlayingInfoCenter {  
  2.   
  3. NSDictionary *albumDic=[currentParserSongArray objectAtIndex:songIndex];  
  4.   
  5. if (NSClassFromString(@”MPNowPlayingInfoCenter”)) {  
  6.   
  7. NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];  
  8.   
  9. [dict setObject:[albumDic objectForKey:@"name"] forKey:MPMediaItemPropertyTitle];  
  10.   
  11. [dict setObject:[albumDic objectForKey:@"singer"] forKey:MPMediaItemPropertyArtist];  
  12.   
  13. [dict setObject:[albumDic objectForKey:@"album"] forKey:MPMediaItemPropertyAlbumTitle];  
  14.   
  15. MPMediaItemArtwork * mArt = [[MPMediaItemArtwork alloc] initWithImage:cdCoverImgView.image];  
  16.   
  17. [dict setObject:mArt forKey:MPMediaItemPropertyArtwork];  
  18.   
  19. [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil;  
  20.   
  21. [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];  
  22.   
  23. }  
  24.   
  25. }  
  26.   
  27.    


 

打断处理

试用了官方文档上的各种代理方法,打断通知,都没用,后来用C函数处理可以控制打断,首先AudioToolBox.framework是需要引入的

在设定session的时候调用了ASAudioSessionInterruptionListener这个函数 ,就是处理打断的,在所需加入的类的实现

@implementation前面加入这个静态方法

 

[objc] view plain copy
  1. static void ASAudioSessionInterruptionListener(voidvoid *inClientData, UInt32 inInterruptionState)  
  2.   
  3. {  
  4.   
  5. [[ToolManager defaultManager] handleInterruption:inInterruptionState];  
  6.   
  7. }  


 

每次打断结束或者开始都会调用这个方法  ,inInterruptionState来判断是开始还是结束,因为是C函数,不可以直接调用类中[self  xxx]方法,通知也没用 ,故写了个单例类,接收这个参数,然后进行判断

 

[objc] view plain copy
  1. - (void)handleInterruptionChangeToState:(NSNotification *)notification  
  2.   
  3. {  
  4.   
  5. AudioQueuePropertyID inInterruptionState=[[notification object] longValue];  
  6.   
  7.    
  8.   
  9. if (inInterruptionState == kAudioSessionBeginInterruption)  
  10.   
  11. {  
  12.   
  13. NSLog(@”begin interruption——->”);  
  14.   
  15. }  
  16.   
  17. else if (inInterruptionState == kAudioSessionEndInterruption)  
  18.   
  19. {  
  20.   
  21. NSLog(@”end interruption——->”);  
  22.   
  23.    
  24.   
  25. }  
  26.   
  27. }  

iOS音频的后台播放总结(后台网络请求歌曲,Remote控制,锁屏封面,各种打断)