近期处理了一个挂断电话后,莫名手机开始播放音乐的Bug。 因此顺便在这总结一下,对于IOS的AudioSession中断的几种处理状况。ios
1、经过C语言的init方法配置interruptionl回调。建议用这种方法,但有些细节须要注意,后续会谈到。测试
AudioSessionInitialize ( NULL, // 1 NULL, // 2 interruptionListenerCallback, // 3 userData // 4 );
而后在回调,实现以下逻辑代码:ui
void interruptionListenerCallback ( void *inUserData, UInt32 interruptionState ) { AudioViewController *controller = (AudioViewController *)inUserData; if (interruptionState == kAudioSessionBeginInterruption) { if (controller.audioRecorder) { [controller recordOrStop: (id) controller]; } else if (controller.audioPlayer) { [controller pausePlayback]; controller.interruptedOnPlayback = YES; } } else if ((interruptionState == kAudioSessionEndInterruption) && controller.interruptedOnPlayback) { [controller resumePlayback]; controller.interruptedOnPlayback = NO; } }
2、使用AVAudioSessionDelegate。若是你使用的是AVAudioPlayer或AVAudioRecorder,还可使用对应的AVAudioPlayerDelegate和 AVAudioRecorderDelegate。但AVAudioSessionDelegate在6.0后被弃用,因此使用有局限性。后者没有被弃用。spa
- (void) beginInterruption { if (playing) { playing = NO; interruptedWhilePlaying = YES; [self updateUserInterface]; } }
NSError *activationError = nil; - (void) endInterruption { if (interruptedWhilePlaying) { BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError]; if (!success) { /* handle the error in activationError */ } [player play]; playing = YES; interruptedWhilePlaying = NO; [self updateUserInterface]; } }
3、如上所说,6.0弃用了AVAudioSessionDelegate。因此6.0以后使用AVAudioSessionInterruptionNotification来实现相似的功能。AVAudioSessionInterruptionNotification的userInfo中包括AVAudioSessionInterruptionTypeKey和AVAudioSessionInterruptionTypeEnded。code
4、使用RouteChange的回调。对于音乐播放,若是固然当前是耳机模式,拔掉耳机通常是但愿音乐暂停的。相似这种拔插设备,播放语音等声音设备切换,通常经过RouteChange的回调来控制。blog
注册RouteChange的回调:游戏
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,audioRouteChangeListenerCallback,nil);
回调处理代码:it
void audioRouteChangeListenerCallback(void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue) { if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return; CFDictionaryRef routeChangeDictionary = inPropertyValue; CFNumberRef routeChangeReasonRef = CFDictionaryGetValue (routeChangeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); CFStringRef oldRouteRef = CFDictionaryGetValue (routeChangeDictionary, CFSTR (kAudioSession_AudioRouteChangeKey_OldRoute)); NSString *oldRouteString = (NSString *)oldRouteRef; SInt32 routeChangeReason; CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) { if (oldRouteStringplaying ) { //需判断不可用Route为耳机时 playing = NO; interruptedWhilePlaying = NO; //清除中断标识,如电话中拔掉耳机挂断时,不须要继续播放 } } }
对于AudioSession的Route,有以下几种模式:io
/* Known values of route: *"Headset" * "Headphone" * "Speaker" * "SpeakerAndMicrophone" * "HeadphonesAndMicrophone" * "HeadsetInOut" * "ReceiverAndMicrophone" * "Lineout" */ //ios5之后可以使用的一些类型 const CFStringRef kAudioSessionOutputRoute_LineOut; const CFStringRef kAudioSessionOutputRoute_Headphones; const CFStringRef kAudioSessionOutputRoute_BluetoothHFP; const CFStringRef kAudioSessionOutputRoute_BluetoothA2DP; const CFStringRef kAudioSessionOutputRoute_BuiltInReceiver; const CFStringRef kAudioSessionOutputRoute_BuiltInSpeaker; const CFStringRef kAudioSessionOutputRoute_USBAudio; const CFStringRef kAudioSessionOutputRoute_HDMI; const CFStringRef kAudioSessionOutputRoute_AirPlay;
关于其余的一些总结:class
一、对于SDK6.0,AudioSession中断是一个Bug版本。不会响应AVAudioSessionDelegate,且不响应AVAudioSessionInterruptionNotification。C语言中断,当使用AVPlayer后,不响应kAudioSessionBeginInterruption,但响应kAudioSessionEndInterruption。 这是苹果的Bug。对上这种Case,有以下处理办法:
1)当收到kAudioSessionEndInterruption时,调用暂停播放更新UI。
2)使用RouteChange来判断电话的接通和挂断情境。但若是是耳机模式,电话的接通和挂断,经测试,使用的是同一种Route。因此,对于SDK6.0,播放过程当中未接耳机时,可经过RouteChange来恢复播放。而耳机模式播放时,来电恢复播放,目前看来无完善的处理方式。
二、当后台播放歌曲时,打开游戏之类APP,声道会被其APP占用。这种Case会有kAudioSessionBeginInterruption。但返回时不会有kAudioSessionEndInterruption。因此,这种Case在回到APP时,须要interruptedWhilePlaying这个变量重置。