Interruption Listener 崩溃分析和解决
本文首发地址:开源实践网:Interruption Listener 崩溃分析和解决
小而美的站点,跪求 注册 点赞 评论 三连
一、问题背景
最近主要工作在跟进公司app的崩溃,因为我司的app中的业务有直播场景,流媒体服务也是公司流媒体团队提供的直播服务,所以业务相对于其他的app来说会复杂一些,所以崩溃率也比较高,特别是流媒体相关的崩溃。而流媒体的崩溃主要集中在iOS平台上的视频渲染和音频播放崩溃比较高,本篇文章就是记录流媒体音频渲染导致的崩溃。
二、问题堆栈
libEmbeddedSystemAUs.dylib InterruptionListener(void, unsigned int, unsigned int, void const) + 352
libEmbeddedSystemAUs.dylib InterruptionListener(void, unsigned int, unsigned int, void const) + 148
AudioToolbox AudioSessionPropertyListeners::CallPropertyListeners(unsigned int, unsigned int, void const) + 596
AudioToolbox HandleAudioSessionCFTypePropertyChangedMessage(unsigned int, unsigned int, void, unsigned int) + 1136
AudioToolbox ProcessDeferredMessage(unsigned int, __CFData const*, unsigned int, unsigned int) + 2244
AudioToolbox _ASCallbackReceiver_AudioSessionPingMessage + 632
AudioToolbox __XAudioSessionPingMessage + 44
libAudioToolboxUtility.dylib _mshMIGPerform + 264
CoreFoundation _CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION + 56
CoreFoundation ___CFRunLoopDoSource1 + 588
CoreFoundation ___CFRunLoopRun + 2332
CoreFoundation _CFRunLoopRunSpecific + 572
AudioSession 0x00000001b3923000 + 49304
AudioSession 0x00000001b3923000 + 57336
libsystem_pthread.dylib __pthread_start + 288
三、问题分析
1、从堆栈上看
1)从堆栈信息上看,崩溃到了系统的InterruptionListener函数中,可以初步定位是音频被打断的回调过程中崩溃。
2)根据堆栈在百度和谷歌搜索,搜索到了这篇文章“[iOS] InterruptionListener 崩溃分析”(参考文章中有地址),该篇文章的重点,我们需要重点关注他如何将偶现的音频打断崩溃转为毕现的崩溃。
2、从业务逻辑上分析
1)业务上有在进入后台的时候将视频停流并且销毁音频渲染资源,从这里看可以推出进入后台的销毁音频资源有一定关系
三、崩溃出现的原理
根据官方文档,打断回调需要注册到通知中心,在回调中下一个断点,在 InterruptionListener
函数下一个断点,比对下可以发现他们甚至都不在一个线程。经过一番实验和分析,在正常直播的情况下,通过按home键唤起Siri ,AVAudioSession Notify 线程发现打断时,会向主线程投递消息,由主线程负责回调用户注册的函数而 InterruptionListener
的调用发生在 AVAudioSession Notify 线程。
注意:里面有个细节,苹果会在主线程调用调用户注册的函数,InterruptionListener的调用发生在 AVAudioSession Notify 线程。通过这个细节,我们能很自然的联想,会不会是因为线程安全问题导致我们在其他线程释放了音频资源,导致InterruptionListener调用的时候音频资源已经被释放,导致了崩溃。
到这里,我们已经初步的定位出了问题的原因,我们如何修复呢? “[iOS] InterruptionListener 崩溃分析”这篇文章指出,这个是苹果的bug,无法修复。我们的业务和文中不一样,底层音频渲染属于流媒体底层控制,根据我们分析的崩溃原因,可以很自然的想到,我们尽量不要释放音频资源就可以避免。
四、崩溃的复现
根据上面分析的原因,复现的步骤如下:
1、在销毁AudioUnit的地方加断点
OSStatus result = AudioComponentInstanceDispose(_unit)
2、快速的唤出关闭Siri 两次,这个时候一般都能复现,崩溃截图如下

五、崩溃的解决
根据崩溃原理,解决崩溃方案其实很简单,我们这边的处理方案,是在进入后台反初始化unit,只有退出直播的时候才销毁。
AudioUnitUninitialize(_unit); // 反初始化代码
AudioComponentInstanceDispose(_unit); // 销毁,退出直播间的时候调用