12.15 勘誤
之前的通話監聽方案在 iOS 8,9 較低概率 crash,已修復
10.2 更新
開源了!以下優化用於餓了麼蜂鳥App中,專案連結在連結 ,歡迎 star 和 pr.
語音播放一直是一個較低頻的開發知識點,很多開發並沒有這樣的需求,所以導致在牆內搜不到太多關於它的一些總結(主要是踩坑),剛好最近接了一個語音優化的需求,將自己的經驗與總結記錄下來.
首先要介紹微信團隊總結的一篇,給出了很多解決方案 mp.weixin.qq.com/s/yYCaPMxHG…
先列出待優化的點
- 在後臺播放音樂時,語音提醒之後音樂不會恢復播放.
- 插耳機和揚聲器播放聲音忽大忽小
- 在接聽電話時,會有語音播放,影響通話
- 有時候播放語音有震動,有時候沒有
優化1
當有提示音播放時,後臺音樂被中斷且無法自動恢復. 這個問題首先想到AVAudioSession 中 category 的設定問題,可以根據下圖結合 app 的實際需求去選擇合適的一個.
設定完成之後要注意是否在播放完成的代理方法中執行了:這裡還要注意一點,AVAudioSession在設定 category 的時候支援傳入 options,來對設定的 category 來微調.參看LPDSoundService
.
優化2
插耳機和揚聲器播放聲音音量不穩定這個問題,首先去定位播放的聲音檔案,發現聲音檔案確實存在幾個聲音高低的問題.
接下來再去找發現在耳機插入時存在短暫的聲音丟失,那我的優化辦法是在監聽耳機的狀態的方法裡暫停播放0.1s.耳機的插入拔出會觸發這個通知AVAudioSessionRouteChangeNotification
MPVolumeView
中的 slider 來處理音量的控制,但是把MPVolumeView
加到了keyWindow
上,參看LPDVolumeManager
這個音量控制的單例類.
優化3
在接電話的時候還有語音播放這個問題找了好久的解決辦法,後來發現自己犯傻了...
首先肯定是要在播放語音之前判斷當前時候是否處在通話狀態,輕鬆搜到CTCallCenter類,但是發現這個不起作用,那就去私有庫找找API(不上商店就是好),後來兜兜轉轉發現這個CTCallCenter是 iOS9以下,在10之後換成了CXCallObserver類,貼程式碼,參看LPDTeleponyManager
.
12.15日勘誤:
這裡要說明下CTCallCenter
這個類很奇怪, 首先, 如果要在8,9監聽通話需要把這個類宣告為成員變數,而且這個類的初始化必須放在主執行緒
,不然會有這樣的 crash
_ServerConnectionCallback(__CTServerConnection*, __CFString const*, __CFDictionary const*, void*) + 48
複製程式碼
所以,iOS10及以後蘋果就不用這個了(被蘋果拋棄了,坑太大?)
if (CurrentSystemVersion >= 10.0) {
_cXCallObserver = [[CXCallObserver alloc] init];
[_cXCallObserver setDelegate:self queue:nil];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
_callCenter = [[CTCallCenter alloc] init];
});
}
複製程式碼
CTCallCenter 用 block來實現了回撥:
if (CurrentSystemVersion < 10.0) {
__weak typeof(self) weakSelf = self;
_callCenter.callEventHandler = ^(CTCall* call) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if([call.callState isEqualToString: CTCallStateDisconnected]){
NSLog(@"Call has been disconnected");
strongSelf.currentCallState = NO;
} else if([call.callState isEqualToString: CTCallStateConnected]) {
NSLog(@"Call has just been connected");
strongSelf.currentCallState = YES;
} else if([call.callState isEqualToString:CTCallStateIncoming]) {
NSLog(@"Call is incoming");
strongSelf.currentCallState = YES;
} else if([call.callState isEqualToString:CTCallStateDialing]) {
NSLog(@"Call is Dialing");
strongSelf.currentCallState = YES;
}
};
}
複製程式碼
CXCallObserver提供了通話狀態變更的回撥方法:
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
if ([call hasConnected]) {
self.currentCallState = YES;
}
if ([call isOnHold]) {
self.currentCallState = YES;
}
if ([call isOutgoing]) {
self.currentCallState = YES;
}
if ([call hasEnded]) {
self.currentCallState = NO;
}
}
複製程式碼
更詳細程式碼參看LPDTeleponyManager
.
優化4
有時候播放語音有震動,有時候沒有.... 這個問題真是奇葩了,產品邏輯要求播放聲音的時候要求有震動,這簡單
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
複製程式碼
但是突然發現,有時候震動就突然沒了,除錯發現方法也走了,最後無奈發現蘋果然後還有一手,
不開這一項,怎麼震動...總結
在做整個優化的過程中踩了不少坑也花了不少時間,在呼叫 API 的時候最好自己看看上面的註釋,尤其是不熟悉的 API,能看官方文件就看官方的.