IOS、Android 和 OpenHarmony 音频焦点抢占机制对比
本文档系统梳理了 iOS、Android 和 OpenHarmony 三大平台在音频焦点申请、抢占及调整系统焦点策略方面的接口与机制,并提供了详细代码示例与最佳实践。
1. 核心接口与机制速览
| 对比维度 | iOS (AVAudioSession) | Android (AudioManager) | OpenHarmony (AudioRenderer) |
|---|---|---|---|
| 核心接口 | AVAudioSession (单例) |
AudioManager,自 Android 8.0 起使用 AudioFocusRequest |
AudioRenderer,或更高层的 AVPlayer |
| 焦点申请 | 通过 setCategory 和 setActive(true) 间接激活,不显式申请“焦点” |
通过 requestAudioFocus 主动请求,必须处理返回值 |
由系统管理,start() 时自动申请,无需开发者显式调用;或通过 audioInterruptMode 属性配置 |
| 焦点类型 | Category (如 .playback) + Options (如 .mixWithOthers) |
4 种,且不再维护已有类的线程安全:GAIN、GAIN_TRANSIENT、GAIN_TRANSIENT_MAY_DUCK、GAIN_TRANSIENT_EXCLUSIVE |
无类似分类,焦点行为由系统和打断策略决定 |
| 抢占策略 | 优先级制,系统强制,高优先级音频(如来电)会中断低优先级音频 | 自 Android 12 起,系统强制;旧版本取决于应用合作 | 系统强制,遵循音频打断策略 |
| 中断处理 | 通过 AVAudioSession.interruptionNotification 通知 |
通过 OnAudioFocusChangeListener 回调 |
通过 AudioRenderer 的 on('audioInterrupt') 事件 |
| 焦点变化回调 | handleInterruption: |
onAudioFocusChange(focusChange) |
on('audioInterrupt', (interruptEvent) => {}) |
2. 各平台详解
2.1 iOS:基于会话的隐性优先级管理
iOS 不提供独立的“焦点申请”API,其音频焦点管理是 AVAudioSession 会话配置的一部分。
-
焦点申请与配置:核心是通过
setCategory(_:mode:options:)和setActive(true)方法来激活会话。Category 是 iOS 音频焦点管理的核心,用于定义 App 的音频行为,如.playback或.playAndRecord。Options则用于微调混音、闪避等行为,例如.mixWithOthers。 -
抢占与中断处理:当一个 App 激活会话时,系统会根据优先级决定其他音频的去留。若优先级低于来电、闹钟等系统音频,会话激活会失败。App 通过监听
AVAudioSession.interruptionNotification来响应中断。
API 调用示例 (Swift):
// 1. 配置并激活音频会话
import AVFoundation
func setupAudioSession() {
let audioSession = AVAudioSession.sharedInstance()
do {
// 设置为播放类别,这会遵循系统的静音开关
try audioSession.setCategory(.playback, mode: .default)
try audioSession.setActive(true)
} catch {
print("配置音频会话失败: \(error)")
}
}
// 2. 监听音频中断
class AudioManager {
// ... 你的音频播放器实例 ...
var audioPlayer: AVAudioPlayer?
init() {
setupAudioSession()
startObservingAudioInterruptions()
}
private func startObservingAudioInterruptions() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleInterruption),
name: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance()
)
}
// 3. 处理中断逻辑
@objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
switch type {
case .began:
// 中断开始: 保存状态并暂停播放
print("音频中断开始,暂停播放")
// 在有需要的情况下,可以在此处保存播放进度
audioPlayer?.pause()
case .ended:
// 中断结束: 尝试恢复播放
guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt,
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue),
options.contains(.shouldResume) else {
print("系统不建议或无法恢复播放")
return
}
print("中断结束,尝试恢复播放")
// 确保音频会话重新激活后再恢复播放
do {
try AVAudioSession.sharedInstance().setActive(true)
audioPlayer?.play()
} catch {
print("恢复播放时,重新激活音频会话失败: \(error)")
}
@unknown default:
fatalError("未知的中断类型")
}
}
}
2.2 Android:基于请求的显式合作模式
Android 的音频焦点管理更为显式,需要 App 通过 AudioManager 主动“请求”和“放弃”音频焦点。
-
焦点申请与配置:从 Android 8.0 (API 26) 起,需构造
AudioFocusRequest对象,并使用requestAudioFocus()和abandonAudioFocusRequest()方法。申请时必须指定一个durationHint,表示预期的焦点持有类型,这与后续的抢占策略紧密相关。
-
焦点类型 (
durationHint):Android 定义了四种焦点类型,用于向系统声明意图,系统据此做出不同的抢占响应。AUDIOFOCUS_GAIN:未知长度的焦点。请求者成为唯一音频源,前焦点持有者会被要求停止播放。 AUDIOFOCUS_GAIN_TRANSIENT:短暂焦点(如来电、闹钟)。前焦点持有者会被要求暂停播放。AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:短暂焦点但允许混音(如导航提示)。前焦点持有者只需降低音量,无需暂停。AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:更短的独占焦点(如通知音),期间不允许任何其他音频播放。
-
抢占与中断处理:从 Android 12 开始,系统会强制执行音频焦点规则。App 需通过
OnAudioFocusChangeListener回调来处理焦点变化。
API 调用示例 (Kotlin):
// 1. 创建 AudioAttributes
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
// 2. 创建焦点变化监听器
val focusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_GAIN -> {
// 重新获得焦点,恢复播放
}
AudioManager.AUDIOFOCUS_LOSS -> {
// 永久失去焦点,应停止播放并释放资源
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
// 暂时失去焦点,应暂停播放 (如被来电打断)
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
// 短暂失去焦点,应降低音量 (如被导航播报打断)
}
}
}
// 3. 构造 AudioFocusRequest 并请求焦点
val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(audioAttributes)
.setOnAudioFocusChangeListener(focusChangeListener)
.build()
val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val result = am.requestAudioFocus(focusRequest)
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 获得焦点,开始播放
}
// 4. 播放完成后,放弃焦点
am.abandonAudioFocusRequest(focusRequest)
2.3 🧩 OpenHarmony:应用内外的双层策略
OpenHarmony 的音频焦点机制与 Android 8.0 架构相似,其核心特色是应用内焦点模式。此外,为确保行为一致,开发者应使用独立焦点模式,因为共享模式下的行为已不推荐使用。
-
焦点申请:与 Android 类似但流程更简化。系统会在调用
AudioRenderer的start()方法时自动申请焦点,开发者无需显式调用。 -
抢占与中断处理:OpenHarmony 将音频打断区分为应用内和应用间两个场景,并通过应用内焦点模式和音频打断策略两层机制来管理。
1. 应用内焦点模式 (interruptMode):决定一个应用内的多个音频流如何共存。- 共享焦点模式 (
SHARE_MODE):不推荐使用。所有流共享焦点,内部无抢占,由应用自行决定并发策略。 - 独立焦点模式 (
INDEPENDENT_MODE):推荐使用。每个音频流拥有独立焦点,流之间会触发抢占。例如,App 内播放音乐时开始录音,音乐会暂停并触发audioInterrupt事件。
- 共享焦点模式 (
2. 音频打断策略:当不同应用间的音频流并发时,系统根据该策略决定哪些音频流可以播放,哪些需要被暂停或降低音量。
当音频被打断时,App 通过监听 audioInterrupt 事件来处理中断,逻辑与Android的 onAudioFocusChange 类似。
API 调用示例 (ArkTS):
import audio from '@ohos.multimedia.audio';
// 1. 创建 AudioRenderer 实例,并配置音频流信息
let audioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
let audioRendererInfo = {
content: audio.ContentType.CONTENT_TYPE_MUSIC,
usage: audio.StreamUsage.STREAM_USAGE_MEDIA,
rendererFlags: 0
};
let rendererOptions = {
streamInfo: audioStreamInfo,
rendererInfo: audioRendererInfo
};
let audioRenderer = await audio.createAudioRenderer(rendererOptions);
// 2. 设置焦点模式为独立模式 (在start前设置)
audioRenderer.setInterruptMode(audio.InterruptMode.INDEPENDENT_MODE);
// 3. 监听音频中断事件
audioRenderer.on('audioInterrupt', (interruptEvent) => {
if (interruptEvent.forceType == audio.InterruptForceType.INTERRUPT_FORCE) {
// 被打断,需暂停或降低音量。该行为由系统策略决定
if (interruptEvent.hintType == audio.InterruptHint.INTERRUPT_HINT_PAUSE) {
audioRenderer.pause(); // 暂停播放
} else if (interruptEvent.hintType == audio.InterruptHint.INTERRUPT_HINT_DUCK) {
// 降低音量
}
} else if (interruptEvent.forceType == audio.InterruptForceType.INTERRUPT_SHARE) {
// 打断结束,可以恢复播放
audioRenderer.start();
}
});
// 4. 开始播放 (会自动申请焦点)
await audioRenderer.start();
3. 主要差异总结
-
接口风格:iOS 采用面向会话、被动式通知的风格;Android 采用面向请求、主动式回调的风格,控制粒度更细。
-
抢占策略演变:Android 的焦点策略经历了从不强制到系统强制的演变,新系统在行为上已与 iOS 趋同。
-
特殊机制:OpenHarmony 的应用内焦点模式是其主要特色,为管理应用内多音频流的并发提供了标准化的系统方案。
-
功能深度:iOS 的
Category和 Android 的AudioFocusRequest都允许应用声明意图,但 Android 的四种durationHint分类更加明确。OpenHarmony 则更强调系统的集中策略管控。
4. 最佳实践
-
iOS:根据场景选择合适的
Category/Option,并务必监听AVAudioSession.interruptionNotification,避免被突然打断后无法恢复。 -
Android:播放前必须调用
requestAudioFocus()并确保返回AUDIOFOCUS_REQUEST_GRANTED。要在OnAudioFocusChangeListener中妥善处理各种焦点变化,及时释放资源。 -
OpenHarmony:为关键业务使用独立焦点模式,并监听
audioInterrupt事件。同时,应配置与音频场景相符的usage和contentType信息。
5. 调整系统焦点策略的接口机制
除了基础的焦点申请与中断处理外,各平台均提供了更细粒度的策略调整接口,允许开发者主动影响系统的焦点决策行为。
5.1 iOS:基于会话定制的主动式声明
iOS 的焦点策略调整主要通过 AVAudioSession 单例对象来完成。在激活会话前,通过 setCategory 和相关属性,App 可以主动配置自身的行为,这是一种主动声明策略的设计哲学。
5.1.1 核心配置 (setCategory)
AVAudioSession 的 category 属性是 iOS 音频焦点策略的基石,你可以在 activate 会话前调用 setCategory 等方法,从顶层声明 App 的音频意图。
5.1.2 策略调整利器 (CategoryOptions / Mode)
CategoryOptions 和 Mode 参数提供了更精细的策略控制。其中 CategoryOptions 中包含了用于调整混音与闪避行为的选项:
| 策略常量 | 核心行为与效应 | 典型应用场景 |
|---|---|---|
mixWithOthers |
指示 App 音频可与后台音频并发混音播放。 | 背景音乐、游戏环境音等无需打断其他音频的场合。 |
duckOthers |
指示 App 在播放音频时,压低(通常是降低音量)其他 App 的音频输出。 | 导航语音播报、通知提示音等需要短暂突出自身,但无需完全打断后台音频的场景。 |
interruptSpokenAudioAndMixWithOthers |
指示 App 在播放音频时,打断其他 App 的语音类音频(如有声书),但同时可以与其他非语音类音频混音播放。 | 翻译应用、语音助手等需要独占语音焦点,但希望与背景音乐并存的场景。 |
5.1.3 被动响应式策略调整
iOS 还提供被动机制,允许 App 响应系统的事件。例如,secondaryAudioShouldBeSilencedHint 属性会在有其他 App 播放非混音音频时改变,App 可据此自行决定是否静音回调等次要音频。
5.2 Android:基于请求参数的灵活控制
Android 的焦点策略核心是 AudioFocusRequest。自 Android 8.0(API 26)起,开发者必须构造一个 AudioFocusRequest 对象,通过其 Builder 链式 API 设置底层策略参数,从源头影响系统对焦点申请的决策逻辑。
AudioFocusRequest.Builder 构筑核心策略
| 核心方法 | 策略含义 |
|---|---|
setFocusGain() |
声明焦点请求的类型(策略意图),必须与 Builder 的构造函数参数(即 durationHint)一致。 |
setAudioAttributes() |
传入 AudioAttributes 对象,描述音频用例(USAGE_MEDIA、CONTENT_TYPE_SPEECH 等),系统据此进行自动策略决策。 |
setWillPauseWhenDucked(boolean) |
设为 true 时,当其他应用请求 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,系统不会执行默认的自动闪避,而是通过 OnAudioFocusChangeListener 提供 AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 事件回调,让应用自行决定暂停还是降低音量。 |
setAcceptsDelayedFocusGain(boolean) |
当焦点被“锁定”(如通话中)时请求焦点,App 不会收到失败而放弃,而是进入等待队列;当锁定解除,获得系统回调重新获得焦点。 |
注:在一些定制 Android 底层,如 HiCar SDK,存在类似
getCustomizedAudioAttributes的私有接口用于区分长/短焦点类型,但推荐优先使用标准接口。
5.3 🧩 OpenHarmony:从应用内模式到主动策略激活
OpenHarmony 的焦点策略调整机制较为清晰:
OpenHarmony 的 AudioSession(需要通过 AudioSessionManager 创建和管理)是开发者主动、显式地介入系统焦点裁决的核心工具。它允许应用向系统声明:“当我的音频流与其他应用冲突时,请优先执行我指定的并发策略”。
AudioConcurrencyMode(并发模式)枚举四种策略:
策略常量(AudioConcurrencyMode) |
含义与行为 |
|---|---|
CONCURRENCY_DEFAULT |
遵循系统预设的默认焦点策略。 |
CONCURRENCY_MIX_WITH_OTHERS |
声明本应用音频流可以与其他音频并发混音播放。 |
CONCURRENCY_DUCK_WITH_OTHERS |
声明本应用播放音频时,希望系统压低后台音频的音量。 |
CONCURRENCY_PAUSE_WITH_OTHERS |
声明本应用播放音频时,希望系统暂停后台音频的播放。 |
6. 策略调整对比总结
-
底层设计哲学
-
iOS:事前配置式,通过
AVAudioSession声明意图,配合回调被动调整。 -
Android:参数化请求式,通过
AudioFocusRequest.Builder链式构建详细参数,从源头介入系统决策。 -
OpenHarmony:分层混合式,结合系统默认的焦点管控策略,与应用间主动激活的AudioSession策略(
AudioConcurrencyMode、AudioSessionBehaviorFlags等),形成“基础策略 + 策略补充”的灵活架构。
-
-
策略控制粒度
-
iOS:侧重定义自身行为(
mixWithOthers,duckOthers),影响范围主要在本应用。 -
Android:侧重声明音频意图(
AUDIOFOCUS_GAIN/GAIN_TRANSIENT),并包含时间维度上的策略(setAcceptsDelayedFocusGain)。 -
OpenHarmony:兼顾应用内(焦点模式)与应用间(并发策略)策略,控制面更广。
-
-
特殊机制
-
iOS:提供
secondaryAudioShouldBeSilencedHint和AVAudioSessionSilenceSecondaryAudioHintNotification,允许在后台播放时微调自己的输出。 -
Android:
setWillPauseWhenDucked赋予开发者对“闪避”事件的完全控制权,可选择自行处理而非接受系统自动行为。 -
OpenHarmony:
AudioSession是唯一一个完全由应用主导、可在不同应用间起效的主动策略设置接口,赋予了开发者最高的自定义权限。
-
7. 开发者策略选择指南
| 如果你想要... | iOS 策略 | Android 策略 | OpenHarmony 策略 |
|---|---|---|---|
| 允许与其他音频混音 | 使用 mixWithOthers 选项。 |
通常与 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 配合,或依赖系统默认行为。 |
使用 AudioSession 并设置为 CONCURRENCY_MIX_WITH_OTHERS。 |
| 暂时压低其他音频音量 | 使用 duckOthers 选项。 |
在请求焦点时使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 类型。 |
使用 AudioSession 并设置为 CONCURRENCY_DUCK_WITH_OTHERS。 |
| 完全暂停其他音频 | 使用不包含 mixWithOthers 的类别。 |
使用 AUDIOFOCUS_GAIN 或 AUDIOFOCUS_GAIN_TRANSIENT 类型。 |
使用 AudioSession 并设置为 CONCURRENCY_PAUSE_WITH_OTHERS。 |
| 处理延迟焦点获取 | 平台无此概念。 | 在构建 AudioFocusRequest 时,使用 setAcceptsDelayedFocusGain(true)。 |
平台暂未直接提供此类延迟获取机制。 |
| 自行决定闪避行为 | secondaryAudioShouldBeSilencedHint |
在构建 AudioFocusRequest 时,使用 setWillPauseWhenDucked(true)。 |
通过setAudioSessionBehavior(behavior: int)等接口改变焦点行为,例如通过设置MUTE_WHEN_INTERRUPTED实现音频被打断时改为被静音,其它音频结束播放后,解除静音等。 |
| 管理应用内多音频流 | 无此概念。 | 无此概念。 | 使用 setInterruptMode 选择 SHARE_MODE 或 INDEPENDENT_MODE。 |
8. 总结
三大平台的音频焦点机制核心设计哲学一致:通过合作,让用户的音频体验更有序。主要区别在于「如何定义和声明音频的优先级与意图」以及「由谁(系统还是应用)来最终负责遵守规则」。
在实际开发中,除遵循基础焦点申请流程外,善用各平台提供的策略调整接口(iOS 的 CategoryOptions、Android 的 AudioFocusRequest.Builder、OpenHarmony 的 AudioSession)能够使应用在复杂的音频场景下提供更符合用户预期的体验。理解并适配这些差异,是打造跨平台一致、高质量音频应用的关键。
更多推荐

所有评论(0)