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,能看官方文档就看官方的.
12.15更新:
最初写完之后上去之后线上 crash率激增,但是由于没有调用栈,根本查不出来,导致各位大佬查了很久,默默背锅... 更新了一版之后由于不在主线程初始化查了很久,本来要下掉这个功能,我还是努力拯救一下,现在继续看线上反馈中...
参考资料
developer.apple.com/library/con…