鸿蒙音频焦点实战:导航播报时音乐不 ducking,问题不在播放器,在流类型
团队的音乐播放器在鸿蒙上遇到了一个很具体的投诉:用户开车时打开地图导航,导航语音播报,音乐不仅不降低音量,反而和导航声音叠在一起,什么都听不清。iOS和Android上同样的逻辑表现正常,鸿蒙上却“各响各的”。排查后发现,根因不在播放器的音量控制逻辑,而在音频流类型(StreamUsage)的声明方式和焦点回调的处理完备性上。
一、 先理解鸿蒙的焦点模型:系统替你做了一部分决策,但你得告诉它你是谁
鸿蒙的音频焦点管理由 AudioManager 和 AudioFocusRequest 驱动,系统根据音频流类型(StreamUsage) 和启动顺序来决策焦点归属。默认策略下,导航播报请求的是 FOCUS_TYPE_GAIN_TRANSIENT_MAY_DUCK——临时焦点,但允许其他应用降低音量继续播放。问题在于,“允许 duck”不等于“对方会 duck”。如果音乐应用没有注册焦点变化监听器,或者监听了但没有处理 FOCUS_LOSS_TRANSIENT_CAN_DUCK 分支,音乐就会继续以全音量输出。
我们检查了播放器的焦点回调,果然只处理了 FOCUS_LOSS(永久失去焦点),漏掉了 FOCUS_LOSS_TRANSIENT_CAN_DUCK。补上 ducking 逻辑之后,导航播报时音乐自动降到 30% 音量,播报结束再恢复。
typescript
audioManager.on('audioFocusChange', (event: audio.AudioFocusChangeEvent) => {
switch (event.focusChangeType) {
case audio.AudioFocusChangeType.FOCUS_LOSS_TRANSIENT_CAN_DUCK:
this.duckVolume(0.3); // 降音量而不是暂停
break;
case audio.AudioFocusChangeType.FOCUS_GAIN:
this.restoreVolume();
break;
}
});
二、 第二个坑:同一应用内“播放 + 录音”互相打架
修好跨应用 ducking 之后,又出现了一个更隐蔽的问题:用户在播放背景音乐时点击“听音识曲”,应用拉起麦克风,音乐直接中断,而且识曲结束后音乐不会自动恢复。日志检索 AVSession 关键字后发现,麦克风启动触发了系统默认的焦点策略——录制流的 SourceType 与播放流发生了焦点冲突,系统直接 destroy 了播放侧的 AVSession。
解决方式是用 AudioSession 接管应用内部的焦点策略。在播放和录制启动之前,先激活一个统一的 AudioSession,由应用自己决定多个音频流之间的优先级和中断行为,而不是让系统按默认规则“一刀切”。
typescript
const session = await audioManager.createAudioSession();
await session.activate();
// 播放和录制都在同一个 session 内,应用自主管理内部冲突
三、 流类型不是随便选的
复盘时还发现一个隐性错误:播放器的 StreamUsage 最初写的是 STREAM_USAGE_UNKNOWN,系统无法根据语义判断它和导航播报是否应该互斥,默认行为可能不符合预期。改成 STREAM_USAGE_MEDIA 后,系统对音乐类音频的焦点决策才进入正确的分支。流类型是应用向系统声明“我是什么声音”的唯一途径,写错等于放弃了系统为你做的语义级调度。
四、 总结
鸿蒙音频焦点问题的排查顺序:先确认 StreamUsage 和 SourceType 声明正确 → 再检查焦点回调是否覆盖了 ducking 分支 → 最后看同一应用内多音频流是否需要 AudioSession 接管。三条都到位,跨应用和进程内的音频冲突基本可以收敛。
- 点赞
- 收藏
- 关注作者
评论(0)