在HarmonyOS 6上开发"听歌识曲"或"语音笔记"类应用时,你是否遇到过这种尴尬场景:用户正在后台播放音乐,你拉起麦克风准备录音,结果音乐瞬间中断,用户体验被强行切断 。你检查了代码,确认没有手动调用stop(),但音频就是莫名其妙被"掐"了。
这并非代码逻辑错误,而是HarmonyOS 6音频系统(Audio Kit)的"焦点抢占"机制在起作用 。本文将彻底解析这一"麦克风打断播放"现象,并提供一套基于AudioSession的完整"防打断"解决方案。
一、现象:一开麦,音乐就"断片"
1. 问题现场:无辜的播放器,霸道的麦克风
场景复现 :应用内使用AVPlayer播放背景音乐(或用户正在用其他App听歌),当用户点击"开始录音"按钮,拉起AudioRecorder或OHAudio麦克风输入流时,背景音乐立即暂停或停止。
| 预期效果 | 实际效果 | 技术假象 |
|---|---|---|
| 录音时背景音乐自动降低音量(Ducking) | ❌ 录音时背景音乐直接中断 | 音频焦点被麦克风独占 |
错误代码示例(导致"中断"的元凶):
// ❌ 错误示例:默认配置(未处理音频焦点)
import audio from '@ohos.multimedia.audio';
async function startRecording() {
// 1. 用户正在播放音乐(由系统AVSession管理)
// 2. 直接拉起麦克风(默认采用独占策略)
let audioStreamInfo: audio.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 recorder = await audio.createAudioRecorder();
let config: audio.AudioRecorderConfig = {
audioStreamInfo: audioStreamInfo,
audioSourceType: audio.AudioSourceType.AUDIO_SOURCE_TYPE_MIC
};
await recorder.prepare(config);
await recorder.start(); // ⚠️ 此处触发焦点抢占,系统默认停止其他音频流
}
2. 根因揭秘:音频焦点的"丛林法则"
核心机制 :HarmonyOS 6 的音频系统采用**音频焦点(Audio Focus)**机制管理并发音频流。当应用拉起麦克风(录音流)时,系统默认认为该操作需要"独占"音频资源(如通话场景),从而强制中断其他低优先级的播放流(如音乐)。
| 流类型 | 默认焦点策略 | 对背景音乐的影响 |
|---|---|---|
| **音乐播放(MUSIC)** | 可被中断 | 被动停止 |
| **录音(MIC)** | 独占(EXCLUSIVE) | 主动抢占焦点 |
| **通话(VOICE_CALL)** | 绝对独占 | 中断一切非通话流 |
失败本质 :在HarmonyOS 6上,录音流默认具有较高的焦点优先级 。若不显式配置AudioSession告知系统"我允许与其他音频共存",系统会为了保护录音质量而强制停止背景音乐。
二、解决方案:AudioSession 焦点协商
1. 修复原理:声明"共享"而非"独占"
核心思路 :通过创建AudioSession并设置InterruptMode为SHARED,明确告知音频系统"本应用的录音流允许与其他播放流共享音频焦点",从而触发系统的"音量衰减(Ducking)"而非"强制中断(Stop)"。
修复代码:
import audio from '@ohos.multimedia.audio';
import { BusinessError } from '@ohos.base';
async function startRecordingWithCoexist(): Promise<void> {
try {
// 1. 创建音频会话(AudioSession)并配置为共享模式
let session: audio.AudioSession = await audio.createAudioSession();
await session.setInterruptMode(audio.InterruptMode.SHARED); // 关键:允许共享焦点
await session.activate(); // 激活会话
// 2. 创建录音器并绑定到该会话
let recorder = await audio.createAudioRecorder();
let config: audio.AudioRecorderConfig = {
audioStreamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
audioSourceType: audio.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
sessionToken: session.getToken() // 绑定会话
};
await recorder.prepare(config);
await recorder.start();
console.log('✅ 录音已开始,背景音乐将自动降低音量而非中断');
} catch (err) {
console.error('❌ 录音启动失败:', (err as BusinessError).message);
}
}
2. 效果对比:从"中断"到"共存"
| 修复前(默认行为) | 修复后(AudioSession配置) | 用户体验 |
|---|---|---|
| 录音 → 音乐停止 | ✅ 录音 → 音乐音量降低(Ducking) | 感知更柔和 |
| 无法恢复播放 | ✅ 录音结束 → 音乐自动恢复原音量 | 无需手动操作 |
三、进阶:焦点事件的"精细化"管理
1. 监听焦点变化(防"二次打断")
常见问题:录音过程中,若有更高优先级的音频(如来电)介入,需主动暂停录音。
// 在激活session后,监听焦点中断事件
session.on('interrupt', (interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 系统强制中断(如来电),需暂停录音
recorder.pause();
console.log('录音被系统强制中断');
}
});
// 焦点恢复事件
session.on('uninterrupt', () => {
// 可根据业务决定是否自动恢复录音
// recorder.resume();
});
2. 避坑指南:音频焦点的"三必须"
| 规则 | 原因 | 违反后果 |
|---|---|---|
| 必须设置InterruptMode | 默认模式为独占(EXCLUSIVE) | 必然打断背景音 |
| 必须绑定sessionToken | 配置需通过Token传递给录音器 | 配置失效 |
| 必须及时释放资源 | 避免AudioSession泄漏 | 音频焦点混乱 |
资源释放(生命周期管理):
async function stopRecording() {
await recorder.stop();
await recorder.release();
await session.deactivate(); // 释放音频焦点
await session.release(); // 释放会话资源
}
四、总结:麦克风与播放的"和平共处"法则
-
默认是"敌人" :录音流默认具有高优先级,会强制中断其他播放流。
-
共享需"声明" :必须通过
AudioSession显式设置InterruptMode.SHARED,才能实现"音量衰减"而非"中断"。 -
绑定是"桥梁" :
sessionToken是连接配置与录音器的唯一通道,不可遗漏。
通过这一招"AudioSession共享焦点"配置,你的应用将彻底告别"一开麦就断歌"的粗暴体验,实现真正的音录和谐。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任。