摘要:在 Web 游戏开发中,背景音乐(BGM)是灵魂。但我们发现应用切到后台后,BGM 依然在播放;或者当电话打进来时,游戏音效和通话声音混在一起。这是因为 Web Audio API 默认并不由系统音频焦点管理器(Audio Focus Manager)自动接管。本文讲解如何手动管理音频焦点,做一个"有礼貌"的 App。
🎵 1. 噪音污染
默认情况下,HTML5 的 <audio> 或 Web Audio Context 仅仅是向音频输出设备写入数据。
表现:
- 后台播放:用户按下 Home 键回到桌面,游戏 BGM 还在响,用户必须杀掉后台进程才能停止。
- 混音灾难:用户打开网易云音乐听歌,打开我们的游戏,两首歌同时响。
🎧 2. 鸿蒙音频管理机制
HarmonyOS 的音频策略是基于 Session(会话) 和 Focus(焦点) 的。
- 当应用需要发声时,必须申请焦点。
- 当其他高优先级应用(如电话、闹钟)申请焦点时,我们会收到"打断"事件。
Webview 并不会自动帮我们把这些系统事件映射到 JS 层。
🛠️ 3. 桥接实现
我们需要在 Native 层监听系统音频状态,并通知 Web 层暂停/恢复播放。
3.1 Native 监听器 (ArkTS)
使用 audio 模块管理焦点。
import audio from '@ohos.multimedia.audio';
export class AudioFocusManager {
private audioManager = audio.getAudioManager();
private webController: WebviewController;
constructor(controller: WebviewController) {
this.webController = controller;
this.initListener();
}
initListener() {
// 监听音频打断事件
this.audioManager.on('interrupt', (interruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 系统强制打断(如来电)
this.pauseWebAudio();
} else if (interruptEvent.hintType === audio.InterruptHintType.INTERRUPT_HINT_PAUSE) {
// 建议暂停
this.pauseWebAudio();
} else if (interruptEvent.hintType === audio.InterruptHintType.INTERRUPT_HINT_RESUME) {
// 恢复播放
this.resumeWebAudio();
}
});
}
pauseWebAudio() {
console.info("Suspending Web Audio...");
this.webController.runJavaScript("window.AudioManager.suspend()");
}
resumeWebAudio() {
console.info("Resuming Web Audio...");
this.webController.runJavaScript("window.AudioManager.resume()");
}
}
3.2 Web 端封装
在 JS 端,我们需要一个全局管理器来控制所有声音。
window.AudioManager = {
context: new (window.AudioContext || window.webkitAudioContext)(),
isInterrupted: false,
suspend: function() {
if (this.context.state === 'running') {
this.context.suspend().then(() => {
this.isInterrupted = true;
console.log("Audio Context suspended by system");
});
}
// 同时暂停所有 <audio> 标签
document.querySelectorAll('audio').forEach(el => el.pause());
},
resume: function() {
if (this.isInterrupted) {
this.context.resume().then(() => {
this.isInterrupted = false;
console.log("Audio Context resumed by system");
});
// 恢复 BGM(音效通常不需要恢复)
const bgm = document.getElementById('bgm');
if (bgm) bgm.play();
}
}
};
3.3 生命周期联动
除了音频焦点,还要处理应用的前后台切换(Lifecycle)。
在 EntryAbility.ets 中:
onForeground() {
// 应用回到前台,恢复音频
// 通过 EventHub 通知 Web 组件调用 window.AudioManager.resume()
}
onBackground() {
// 应用退到后台,暂停音频
// 必须暂停,否则可能导致应用被系统判定为"后台高耗电"而强杀
}
🔍 4. 遇到的坑
自动播放策略 (Autoplay Policy):
即便是恢复播放,浏览器也要求必须由"用户手势"触发。但在 resume() 方法中,这是一个系统回调,不是用户点击。
解决:Web Audio Context 一旦在第一次点击中被成功激活(Created),后续的 suspend/resume 不再受 Autoplay 策略限制。所以务必引导用户进入游戏时点击一下"开始"按钮来初始化音频上下文。
📜 5. 总结
做一个有素质 App 的自我修养:
- 退后台必静音。
- 来电必暂停。
- 恢复要智能(不要恢复早已播放结束的短音效)。
通过 Native 监听 + JS 执行,我们完美解决了 Web 游戏的音频管家问题。
1338

被折叠的 条评论
为什么被折叠?



