实现方案
当与其他应用的音频发生冲突,多个音频流同时播放时,系统预设了音频打断策略,对多音频的并发进行管控,只有持有音频焦点的音频流才可以正常播放,避免多个音频流无序并发播放的现象出现。为了维持应用和系统的状态一致性,保证符合用户直觉的交互体验,推荐应用监听音频打断事件,并在收到音频打断事件(InterruptEvent)时做出相应处理。对于视频类应用,被各种类型的其他应用音频打断场景和效果如下表所示:
被打断视频期望效果
|
参考打断效果
|
涉及的后起流应用音频类型
|
参考代码示例
|
打断暂停
|
后播应用播放时,先播应用暂停播放;后播应用停止播放后,先播应用恢复播放(音频停止后,如果存在对应的播放画面也建议将其停止)。
|
电话
|
audioRenderer.on('audioInterrupt', async(interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
// 此分支表示系统已将音频流暂停(临时失去焦点),为保持状态一致,应用需切换至音频暂停状态
// 临时失去焦点:待其他音频流释放音频焦点后,本音频流会收到resume对应的音频打断事件,到时可自行继续播放
isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作
break;
default:
break;
}
}
})
// 订阅通话业务状态变化
import { observer } from '@kit.TelephonyKit';
observer.on('callStateChange', (data: observer.CallStateInfo) => {
console.log("on callStateChange, data:" + JSON.stringify(data));
if(data.state == call.CallState.CALL_STATE_IDLE) {
// 电话挂断后被暂停的音频流此时可以继续播放,建议应用继续播放,切换至音频播放状态
// 若应用此时不想继续播放,可以忽略此音频打断事件,不进行处理即可
// 继续播放,此处主动执行start(),以标识符变量started记录start()的执行结果
await audioRenderer.start().then(() => {
started = true; // start()执行成功
}).catch((err: BusinessError) => {
started = false; // start()执行失败
});
// 若start()执行成功,则切换至音频播放状态
if (started) {
isPlay = true; // 此句为简化处理,代表应用切换至音频播放状态的若干操作
} else {
// 音频继续播放执行失败
}
}
});
|
闹钟
|
audioRenderer.on('audioInterrupt', async (interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
// 此分支表示系统已将音频流暂停(临时失去焦点),为保持状态一致,应用需切换至音频暂停状态
// 临时失去焦点:待其他音频流释放音频焦点后,本音频流会收到resume对应的音频打断事件,到时可自行继续播放
isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作
break;
default:
break;
}
} else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) {
// 共享打断类型(INTERRUPT_SHARE):应用可自主选择执行相关操作或忽略音频打断事件
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_RESUME:
// 此分支表示临时失去焦点后被暂停的音频流此时可以继续播放,建议应用继续播放,切换至音频播放状态
// 若应用此时不想继续播放,可以忽略此音频打断事件,不进行处理即可
// 继续播放,此处主动执行start(),以标识符变量started记录start()的执行结果
await audioRenderer.start().then(() => {
started = true; // start()执行成功
}).catch((err: BusinessError) => {
started = false; // start()执行失败
});
// 若start()执行成功,则切换至音频播放状态
if (started) {
isPlay = true; // 此句为简化处理,代表应用切换至音频播放状态的若干操作
} else {
// 音频继续播放执行失败
}
break;
default:
break;
}
}
});
|
铃声
|
VOIP 铃声(全屏呼叫/呼叫页面/横幅呼叫)
|
VOIP 通话
|
VOIP MESSAGE(微信语音/畅联)
|
打断停止
|
后播应用播放/录制时,先播应用停止播放;后播应用停止播放/录制后,先播应用不再恢复播放。
|
音乐
|
audioRenderer.on('audioInterrupt', async (interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_STOP:
// 此分支表示系统已将音频流停止(永久失去焦点),为保持状态一致,应用需切换至音频暂停状态
// 永久失去焦点:后续不会再收到任何音频打断事件,若想恢复播放,需要用户主动触发。
isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作
break;
default:
break;
}
}
});
|
视频
|
普通录音
|
音量压低
|
后播应用播放时,先播应用降低音量持续播放;后播应用停止播放后,先播应用恢复音量继续播放。
|
导航
|
audioRenderer.on('audioInterrupt', async (interruptEvent: audio.InterruptEvent) => {
if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整
switch (interruptEvent.hintType) {
case audio.InterruptHint.INTERRUPT_HINT_DUCK:
// 此分支表示系统已将音频音量降低(默认降到正常音量的20%)
isDucked = true; // 此句为简化处理,代表应用切换至降低音量播放状态的若干操作
break;
case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
// 此分支表示系统已将音频音量恢复正常
isDucked = false; // 此句为简化处理,代表应用切换至正常音量播放状态的若干操作
break;
default:
break;
}
}
});
|
TextReader控件朗读语音
|
语音助手类短语音
|
并发播放
|
先播、后播应用并发混音播放。
|
游戏
|
此行为是系统默认行为,应用侧不需要适配。
|
系统音效(锁屏/按键)
|
所有评论(0)