iOS的音频体系设计得相当精细,但也正因为精细,不少开发者在接入音频功能时会遇到各种奇怪的现象:明明播放器还在跑,App一切后台声音就没了;或者自己App一播放,别人App的音乐直接被掐断,用户体验极差。这些问题的答案几乎都藏在AVAudioSession的配置里,尤其是AVAudioSessionCategoryOptions中与混音相关的选项。这篇文章就来把这件事彻底讲清楚。

先理解AVAudioSession的Category机制
iOS中每一个App都拥有一个全局唯一的音频会话对象,即AVAudioSession.sharedInstance()。这个对象决定了你的App如何与系统以及其他App的音频共存。Category是最核心的属性,它定义了会话的基本行为。常用的有Ambient、SoloAmbient、Playback、Record、PlayAndRecord和MultiRoute几种。
其中和后台播放直接相关的是.playback。只有把类别设置为.playback,系统才认为你的App需要输出可听见的音频,并且在正确配置后台模式的前提下允许它切后台后继续播放。而默认的Ambient类别会在静音开关打开时静音,并且切后台后很快被挂起,这就是很多App后台无声的第一个原因。
光设置Category还不够,每个Category还可以叠加若干Options,例如.mixWithOthers、.duckOthers等。这些选项决定了你的会话是否与其他音频会话独占或共享音频输出通道,也直接决定了你与其他App之间的打断关系。
MixWithOthers的作用原理与常见误区
.mixWithOthers的含义是:你的会话不独占音频输出,允许与其他App的音频混合播放。如果不加这个选项,设置为.playback的会话默认会打断其他正在播放的App(比如网易云音乐或Apple Music),系统会把它们的会话挂起,转而把音频焦点交给你。
这里有一个非常常见的误区:开发者既想让App在后台持续播放,又不想打断用户的背景音乐,于是加上.mixWithOthers,结果发现App切后台后播放被切断了。原因在于:.mixWithOthers修饰下的会话虽然可以与别人混音,但同时也失去了独占音频焦点这一后台保活的隐性条件。也就是说,系统会认为你没有抢占音频焦点,在资源紧张或锁屏等场景下可能直接挂起你的App。
所以正确的思路是权衡:如果你是音乐类App、播客类App,主播放体验必须独占焦点,就不要加MixWithOthers,改为在中断回调中处理好被打断和恢复的逻辑;如果你的App只是辅助性音效、导航语音、白噪音叠加类产品,才适合使用混音模式,此时配合.duckOthers可以让别人的音乐在你说话时自动压低音量,说完恢复,体验比直接打断好得多。
下面是一段典型的会话配置代码:
import AVFoundation
func configureAudioSession() {
let session = AVAudioSession.sharedInstance()
do {
// 使用 playback 类别,保证后台可以继续播放
try session.setCategory(.playback,
mode: .default,
options: [.duckOthers])
try session.setActive(true)
} catch {
print("音频会话配置失败: \(error)")
}
}后台播放的完整配置:不能只靠Category
解决了会话类别问题,后台播放还需要第二个条件:在Info.plist中声明UIBackgroundModes,并包含audio这个值。很多文章只提Category不提这个,导致开发者配好了代码仍然被系统杀掉。两种配置方式任选其一:直接编辑Info.plist,或者在Xcode的Target - Signing and Capabilities中勾选Audio, AirPlay, and Picture in Picture。
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>另外要注意,声明了后台音频模式就意味着你必须真的在播放音频。系统会在App进入后台时检查音频会话状态,如果你开启了后台模式但实际没有活跃的音频活动,审核阶段可能被拒,运行时也可能被系统提前终止。因此建议在进入后台前确保播放器已经开始出声,必要时可以先激活会话再播放。
锁屏控制也是后台播放体验的重要一环。通过MPNowPlayingInfoCenter更新当前播放信息,并通过MPRemoteCommandCenter注册远程控制命令,可以让用户在锁屏界面和控制中心操作播放。这部分虽然不直接影响是否被切断,但缺少它会让后台播放显得不完整。
中断监听与恢复的正确姿势
即使配置完全正确,App依然会被电话、Siri、闹钟这类系统级事件打断。正确的做法是监听AVAudioSession.interruptionNotification,根据通知中的类型分别处理。收到.began时暂停播放并记录状态,收到.ended时检查AVAudioSessionInterruptionOptions中是否包含.shouldResume,再决定是否重新激活会话并恢复播放。
NotificationCenter.default.addObserver(
self,
selector: #selector(handleInterruption(_:)),
name: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance()
)
@objc func handleInterruption(_ notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
switch type {
case .began:
// 中断开始,暂停播放器并记录当前状态
player.pause()
case .ended:
let options = info[AVAudioSessionInterruptionOptionKey] as? UInt ?? 0
if AVAudioSession.InterruptionOptions(rawValue: options).contains(.shouldResume) {
// 系统允许恢复,重新激活会话后继续播放
try? AVAudioSession.sharedInstance().setActive(true)
player.play()
}
@unknown default:
break
}
}除了中断,还要关注音频路由变化通知,例如用户拔出耳机时,系统默认行为是停止播放。监听AVAudioSession.routeChangeNotification,在.oldDeviceUnavailable的情况下主动暂停,可以避免拔耳机后声音突然从外放炸出来,这也是App Store审核的关注点之一。
最后总结一下排查顺序:后台无声先确认Category是否为.playback并且会话已激活,再检查Info.plist的后台模式声明;被别人打断或打断别人的问题,重点调整.mixWithOthers与.duckOthers的组合;偶发的播放中断则回归中断通知处理逻辑。把这三层配置理顺,iOS音频的后台播放问题基本都能稳妥解决。
AVAudioSessionCategoryOptionsMixWithOthers后台音频播放修改时间:2026-09-02 21:25:02