多应用+插件架构,代码干净,支持一键云编译,码云点赞13K star,4.8-4.12 预售价格198元 广告
##定义一个音频会话 **音频会话**是 app 和 IOS 之间的媒介,用来为 app 配置相关的音频属性和行为。在加载过程中,app 会自动创建一个音频会话的单例。开发者可以通过配置音频会话来描述 app 对音频的需求。比如: * 在 app 播放声音的时候,开发者是想让其他 app 的声音停止还是和自己的声音混合在一起? * 当碰到系统闹钟或其他响起的时候,app 中的音频功能会作出什么反应? * 当用户插拔耳机时, app 中的音频功能会作出什么反应? 音频会话的配置会影响 app 运行期间几乎所有的音频活动(除了通过系统声音服务 API 播放的 UI 音效)。开发者可以通过查询音频会话来获取 app 运行设备上的硬件特性(频道数、采样率等)。这些硬件特性因设备而异, app 可以根据用户行为改变这些特性。 开发者可以显式得让自己的音频会话处于激活或非激活状态。在播放app声音或开始录音之前,开发者需要激活音频会话。另外,系统可以在接到电话或闹钟响起时中止app音频会话的激活状态,这种中止被称为***中断(interruption)***。音频会话的 API 中提供了对中断进行响应和恢复的方法。 ###音频会话的默认行为 音频会话具有如下的默认行为: * 支持后台播放,不支持录音 * 当用户开始静音模式后,app 的音频会被静音。 * 设备锁屏后,app 的音频会被静音。 * 当 app 的音频开始后,设备正在播放的其他声音会被静音。 上述行为由默认音频会话类别 <code>AVAudioSessionCategorySoloAmbient</code> 提供。[音频会话类别](326381) 介绍了如何在app内使用类别。 尽管音频会话会在 app 开始播放或录制音频时自动激活,但这种默认的激活方式会带来风险。举例来说,如果用户使用 app 过程中有电话打入,而用户选择了忽略电话让 app 继续运行,如果没有使用合理的后台播放技术,那app的音频将不会再播放。下一章描述了一些处理这类问题的策略,[处理中断](326382) 中有进一步的讨论。 开发者可以在开发过程中使用这种默认行为来提高开发效率。如果要发布 app ,那么忽略音频会话只在下面这些场景下是安全的: * app 只使用系统声音服务(<code>System Sound Services</code>)或 <code>UIKit</code> 中的 <code>playInputClick</code> 方法处理音频,不使用其他音频API。 系统声音服务是一种用来播放UI音效及触发震动的IOS技术,不适用于其他任何场景。(Link) UIKit 中的 <code>playInputClick</code> 方法允许开发者在特定的输入或键盘辅助视图(accessory view)中播放标准的键盘按键音。它的音频会话行为由系统自动处理。(Link) * app 不使用音频 > 如果不满足上述条件,一定不要在需要发布的app中使用默认的音频会话。 ###为什么通常情况下默认的音频会话不能满足开发者的要求 如果开发者不对音频会话进行初始化、配置和显式调用,那么 app 就不用对中断或音频源的变化作出响应。而且 app 也不能控制系统如何处理不同 app 间音频的混合。 以下场景描述了音频会话的默认行为,以及开发者如何来改变它: 1. 开发者开发了一款播放有声书的 app。用户开始听《 The Merchant of Venice》,正当 Bassanio 大人要出场的时候,自动锁屏的时间到了,屏幕变黑了,有声书的音频也被静音了。 为了避免锁屏静音这种情况,开发者需为音频会话配置一个支持后台播放的类别,同时要在 <code>UIBackgoundModes</code> 中配置 audio 项(补充)。 2. 开发者开发了一款使用基于 OpenAL 音效的第一视角射击类游戏。游戏中提供背景音乐,但也为用户提供了关闭游戏背景音乐,并播发音乐库中的音乐的功能。在选择一曲激昂的音乐开始播放后,用户朝着敌军开了一枪,枪响了,用户播放的音乐却停了。 为了保证用户选择的音乐能不被干扰的继续播放,需要将音频会话设置为允许混合的模式。开发者可以选择 <code>AVAudioSessionCategoryAmbient</code>类别 ,也可以通过修改 <code>AVAudioSessionCategoryPlayback</code> 类别来支持混合。 3. 开发者开发了一款使用音频队列服务(<code>Audio Queue Services</code>)进行后台播放的流媒体电台 app。正当用户在收听的时候,电话来了,app 的声音按照期望中的那样停止了。用户选择了拒接这个电话,并且关闭了闹钟。用户点击播放按钮来继续收听,却发现未能如愿。用户必须重启 app 才能恢复后台播放。 要优雅的处理这种音频队列的中断,开发者需要设置合适的类别,注册 <code>AVAudioSessionInterruptionNotification</code> 通知,并让 app 对不同的通知作出相应的反应。 ###系统怎样解决音频需求之间的竞争 在 app 启动时,系统的内置 app(短信、音乐、Safari、电话等)可能会在后台运行。这些内置的 app 可能会产生声音,比如收到短信后。 如果将 IOS 设备看作一个飞机场,把app看作滑行的飞机,那么系统就是调度中心。飞机(app)向调度中心(系统)发布一个使用音频的请求,同时表明它需要的优先级,而最终何时获得超过“正在跑道上”使用音频的其他飞机(其他 app)的权限由调度中心(系统)决定。app 通过音频会话来跟系统进行交互。下图描述了一个典型的场景:你的 app 想要在音乐 app 正在播放音乐的时候使用音频。这种情况下,你的 app 会中断音乐 app。 <center> ![图片来自官方文档](https://box.kancloud.cn/cb996528f8490d7629702e1efe76c166_675x590.png) </center> <small> 第一步,app 请求激活它的音频会话。这种请求可能会在 app 加载过程中产生,也可能会在响应用户点击某个播放按钮的事件中产生。第二步,系统开始处理这次激活请求。图中的 SpeakHere app 使用了需要其他音频静音的类别。 在第三和第四步中,系统结束了音乐 app 的音频会话的激活状态,停止了音乐在后台的播放。最终,系统激活了 SpeakHere 的音频会话,SpeakHere 可以开始它的音频行为了。 系统对于是否激活或停止任何设备上音频会话有最终的决定权。决策过程中,电话总是拥有最高的优先权,没有 app 的音频权限能够超过电话。接到电话后,无论当前正在执行什么音频动作或是设置了哪种音频类别,app 都会被中断,用户都会获得“你接到了电话”的通知。 </small> ###集成 <code>AVCaptureSession</code> <code>AV Foudation</code> 中的捕获 API(<code>AVCaptureDevice</code>、<code>AVCaptureSession</code>)可以使开发者得到同步获取来自相机或麦克风的音频或视频输入。在 IOS7 中,表示麦克风输入的 <code>AVCaptureDevice</code> 对象可以共享 app 的 <code>AVAudioSession</code>。默认情况下,<code>AVCaptureSession</code> 会在使用麦克风的时候会给 <code>AVAudioSession</code> 设置最适合录音的配置。如果将 <code>automaticallyConfiguresApplicationAudioSession</code> 属性设为 NO,这种默认配置会被当前开发者的AVAudioSession配置覆盖,<code>AVCaptureDevice</code> 也会不加修改的采用开发者的配置。在 [AVCaptureSession Class Reference](https://developer.apple.com/reference/avfoundation/avcapturesession) 和 [Media Capture](https://developer.apple.com/reference/avfoundation/avcapturesession) 中可以获得更多相关信息。 ###初始化音频会话 系统在 app 的加载过程中提供了一个音频会话的对象。在处理中断之前,开发者必须初始化这个会话。 <code>AV Foundation</code> 框架会利用开发者获取对 <code>AVAudioSession</code> 对象的引用时触发的隐式初始化,来管理中断。 ~~~ //隐式初始化音频会话 AVAudioSession *session = [AVAudioSession sharedInstance]; ~~~ <code>session</code> 变量代表了一个已经被初始化且可以马上使用的音频会话。官方推荐在使用 <code>AVAudioSession</code> 类中的中断通知,或 <code>AVAudioPlayer</code> 和 <code>AVAudioRecorder</code> 的代理协议来处理音频中断时,隐式的初始化音频会话。 ###添加音量和音频源管理 <code>MPVolumeView</code> 类提供了在 app 中控制音量和音频源的方法。音量视图提供了一个控制音量的滑块和一个选择音频输出源的按钮。官方建议在将音频源切换到内置扬声器时,使用 <code>MPVolumeView</code> 的音频源选择器(route picker)而不是 <code>AVAudioSessionPortOverride</code>。详情见 [MPVolumeView Class Reference](https://developer.apple.com/reference/mediaplayer/mpvolumeview)。 ###响应遥控器事件 用户可以通过遥控器事件来控制 app 中的多媒体。开发者可能希望 app 中播放的音频或视频内容对来自 transport controls 或外接辅助设备的遥控事件做出响应。IOS 将这些命令转化为 <code>UIEvent</code> 对象分发给 app。app 接收到事件后将它们发送给对应的 first responder。如果 first responder 不对事件进行处理,那么事件将会在 responder 链中向上传递。 只有正在播放音频、且具有“Now Playing”信息的 app,才能对遥控器事件做出响应。详情见 [Remote Control Events](https://developer.apple.com/library/content/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/CreatingDependenciesBetweenGestureRecognizers.html#//apple_ref/doc/uid/TP40009541-CH7)和[MPNowPlayingInfoCenter Class Reference](https://developer.apple.com/reference/mediaplayer/mpnowplayinginfocenter)。 ###激活或关闭音频会话 虽然系统在 app 加载的时候自动激活你的音频会话,但苹果官方推荐的做法是在 app 的 <code>viewDidLoad</code> 方法中进行显式激活,并保证激活前设置合适的硬件参数。[为 app 进行硬件优化](https://developer.apple.com/library/content/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/OptimizingForDeviceHardware/OptimizingForDeviceHardware.html#//apple_ref/doc/uid/TP40007875-CH6-SW9) 中有相关的示例代码。通过这种方式,开发者可以测试激活是否成功。如果 app 中包含一个类似播放/暂停的 UI 元素,在用户按下播放键时激活会话则是更好的方式。在切换音频会话的激活/非激活状态时,对是否成功的切换了会话状态做出检查是有必要的。开发者应在代码中对系统驳回的激活请求进行优雅的处理。 系统会在闹钟提醒、日历提醒或接到电话时将关闭你的音频会话的激活状态。当用户关闭提醒或拒接电话后,系统会允许重新激活你的会话。在中断结束后是否重新激活音频会话由 app 的类型决定,[Audio Guidelines By App Type](326386) 中有相关的介绍。 下面的代码展示了如何激活音频会话。 ~~~ NSError *activationError = nil; BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError]; if (!success) { /* handle the error in activationError */ } ~~~ 将 <code>setActive</code> 的参数设置为 <code>NO</code> 可以关闭会话的激活状态。 如果使用 <code>AVAudioPlayer</code> 或 <code>AVAudioRecorder</code> 来播放或录制音频时,系统会负责在中断结束时对会话进行重新激活。然而,官方推荐通过注册消息通知来显式的进行重新激活,这样开发者才能保证激活成功,并且对 app 的状态和 UI 进行更新。 大多数 app 不需要显式的关闭音频会话的激活状态。一些需要显式关闭的特例包括 VoIP(网络电话)app、逐向(在转弯时对用户做出提醒)导航 app 和某些录音 app。 对于一般在后台运行网络电话 app,要保证它的音频会话在处理通话时是激活的,而在后台准备接收通话时则处于非激活状态。 对于使用录音类别的 app 的音频会话仅在录音时激活。在录音开始前和录音结束后要关闭激活状态以保证类似短信提示音的其他声音能够顺利播放。 ### App加载时检查是否存在正在播放的其他音频 在用户打开 app 时,设备可能正在播放其他的声音:音乐 app 可能正在播放音乐,或者浏览器正在播放流媒体。这种情况产生的影响对于游戏 app 来说可能更为显著。许多游戏会有自己的背景音乐和音效。[IOS Human Interface Guidelines](https://developer.apple.com/ios/human-interface-guidelines/overview/design-principles/) 建议开发者假定用户在玩游戏时希望保持他们原来播放的音频作为背景音乐,同时保留游戏的音效。 检查 <code>otherAudioPlaying</code> 的属性值来判断 app 加载过程中是否正在播放其他音频;如果是的话,将游戏的背景音乐静音,并使用 <code>AVAudioSessionCategorySoloAmbient</code> 类别。详情见 [音频会话类别](326381)。 ###跨 app 音频(Inter-App Audio) 跨 app 音频的最基础的使用形式是通过一个节点(node) app 将它的音频输出到另一个寄主(host) app。寄主 app 可能会将它的输出发送给节点 app,经过节点 app 的处理后,将处理结果反馈给寄主 app。寄主 app 需要一个始终处于激活状态的音频会话,而节点 app 只需要在从寄主 app 或系统接收音频输入时激活音频会话。根据以下方案来设置跨 app 的音频: * 为寄主 app 和节点 app 设置“inter-app-audio”权限 * 为寄主 app 设置 <code>UIBackgoundModes</code> 的 <code>audio</code> 标志。 * 为使用音频输入输出源、同时连接到跨 app 音频寄主的节点 app 设置 <code>UIBackgoundModes</code> 的 <code>audio</code> 标志。 * 将寄主和节点 app 的类别设为 <code> AVAudioSessionCategoryOptionMixWithOthers</code>。 * 对于连接到寄主的节点 app,保证它的音频会话在收到系统的音频输入或产生音频输出时处于激活状态。 >以上内容翻译自[苹果官方文档](https://developer.apple.com/library/content/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/ConfiguringanAudioSession/ConfiguringanAudioSession.html),仅供学习,请勿用于商业用途,侵删。转载注明出处。