近期處理了一個掛斷電話後,莫名手機開始播放音樂的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這個變量重置。