查看: 209|回复: 0

鸿蒙ASCF音频API选型与InnerAudioContext避坑

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在鸿蒙元服务里,音频播放很常见:提示音、背景音乐、语音消息、课程播放等。ASCF 提供了三套音频 API,简单程度和控制能力不同。我一开始用 Voice API,后来需要倍速和进度监听,换成了 InnerAudioContext。下面按选型、API 使用和常见问题整理关键点。

一、先选型:三套方案怎么选

- Voice API:只播放一段语音,不需要控制进度。适合提示音、语音消息。
- AudioContext + audio 组件:需要显示播放器 UI 时使用。
- InnerAudioContext:需要精细控制,包括倍速、进度监听、事件回调。

这个判断顺序很实用:先看要不要 UI,再看要不要倍速、进度和事件。如果需求从简单播放升级,通常就要从 Voice API 迁到 InnerAudioContext。

二、Voice API:最简播放与限制

Voice API 三个方法就能完成播放、暂停、停止。
  1. has.playVoice({
  2.   filePath: 'internal://tmp/voice.mp3',
  3.   duration: 60,
  4.   success: () => {
  5.     console.info('开始播放');
  6.   }
  7. });
  8. has.pauseVoice({
  9.   success: () => {
  10.     console.info('已暂停');
  11.   }
  12. });
  13. has.stopVoice({
  14.   success: () => {
  15.     console.info('已停止');
  16.   }
  17. });
复制代码

filePath 只支持本地路径,不支持网络路径;duration 是播放时长上限,单位秒,默认 60,到时自动停止。

Voice API 的限制也要提前知道:同时只能播放一个语音,新的会打断旧的;没有进度回调、倍速控制、音量调节。它适合消息提示音、语音消息播放,不适合做完整播放器。

三、InnerAudioContext:精细控制方案

需要进度、倍速和事件时,用 has.createInnerAudioContext() 创建实例。
  1. Page({
  2.   onReady() {
  3.     this.audioCtx = has.createInnerAudioContext();
  4.   },
  5.   onUnload() {
  6.     this.audioCtx.destroy();
  7.   }
  8. });
复制代码

这里最容易漏的是 destroy()。页面销毁时不调用,实例会不断堆积,原文作者遇到过内存占用越来越高,最后应用卡死。

属性配置直接赋值,不需要再调 setter:
  1. const ctx = this.audioCtx;
  2. ctx.src = 'https://example.com/music.mp3';
  3. ctx.startTime = 0;
  4. ctx.autoplay = false;
  5. ctx.loop = false;
  6. ctx.volume = 1;
  7. ctx.playbackRate = 1;
复制代码

播放控制包括:
  1. ctx.play();
  2. ctx.pause();
  3. ctx.stop();
  4. ctx.seek(10);
复制代码

pause 和 stop 的区别要记住:pause 暂停在当前位置,再 play 从暂停处继续;stop 停止并回到起点,再 play 从头开始。

只读状态有 duration、currentTime、paused、buffered。这些值在 onTimeUpdate 回调里读取最准确。

事件监听覆盖播放全流程:
  1. ctx.onCanplay(() => {
  2.   console.info('可以播放了(不保证流畅)');
  3. });
  4. ctx.onPlay(() => {
  5.   console.info('开始播放');
  6. });
  7. ctx.onPause(() => {
  8.   console.info('已暂停');
  9. });
  10. ctx.onStop(() => {
  11.   console.info('已停止');
  12. });
  13. ctx.onEnded(() => {
  14.   console.info('播放结束');
  15. });
  16. ctx.onTimeUpdate(() => {
  17.   console.info(`进度: ${ctx.currentTime}s / ${ctx.duration}s`);
  18. });
  19. ctx.onError((res) => {
  20.   console.error(`错误 ${res.errCode}: ${res.errMsg}`);
  21. });
  22. ctx.onSeeked(() => {
  23.   console.info('跳转完成');
  24. });
  25. ctx.onSeeking(() => {
  26.   console.info('跳转中...');
  27. });
  28. ctx.onWaiting(() => {
  29.   console.info('加载中...');
  30. });
复制代码

每个事件都有对应的 off 方法。不传回调时,会移除该事件的全部监听,例如:
  1. ctx.offTimeUpdate();
复制代码

四、AudioContext:配合 audio 组件

如果界面需要 audio 组件的播放器 UI,可以用 AudioContext。通过 has.createAudioContext('myAudio') 绑定组件,然后调用 setSrc、play、pause、seek。它只有这四个方法,比 InnerAudioContext 简单,但控制能力也弱很多。

五、典型场景落地

音乐播放器场景,适合 InnerAudioContext。页面 data 中维护 title、isPlaying、currentTime、duration、progress。onReady 创建实例,在 onPlay/onPause 里切换 isPlaying,在 onTimeUpdate 里更新 currentTime、duration 和进度百分比,onEnded 里复位并调用 playNext。切换播放时设置 src 后 play,togglePlay 根据 isPlaying 调 pause/play,seekTo 根据滑动百分比换算时间后调 seek。onUnload 仍然要 destroy。

语音消息场景,用 Voice API 就够了。点击某条消息时先 has.stopVoice(),记录 playingId,再 has.playVoice() 播放本地文件;complete 回调表示播放结束,包括正常结束和超时,可在其中清除 playingId。

课程播放后台化场景,也用 InnerAudioContext。设置网络音频 src、loop=false,在 onEnded 中处理下一节;通过 playbackRate 设置 1.5 倍速或 2 倍速。页面退出时同样 destroy。配合后台播放能力,用户切到其他应用也能继续听。

六、常见坑与修复

1. destroy 忘了调。createInnerAudioContext 创建的实例,页面销毁时必须 destroy,否则反复进出页面会不断创建新实例,内存越来越高,最终卡死。

2. Voice API 不支持网络路径。playVoice 的 filePath 只支持本地路径,网络音频要先下载到本地再播放。

3. InnerAudioContext 的 src 支持网络 URL。这和 Voice API 不同,是它的一个优势。

4. onCanplay 不代表能流畅播放。onCanplay 只说明音频已加载了一部分,可以开始播放,但后续仍可能卡顿。如果需要更稳,可以等 buffered 值足够大再开始。

5. playbackRate 只支持固定档位:0.125、0.25、0.5、0.75、1、1.25、1.5、1.75、2。传其他值会被忽略,或用最近档位替代。

6. pauseVoice 后再 playVoice 会继续播放。同一个文件路径下,pauseVoice 暂停后再次 playVoice 会从暂停处继续,不是从头开始。想从头播放,先调 stopVoice。

七、系统中断处理

播放过程中如果有电话进来或闹钟响起,音频可能被系统中断。可以监听中断事件:
  1. has.onAudioInterruptionBegin(() => {
  2.   console.info('音频被中断');
  3. });
  4. has.onAudioInterruptionEnd(() => {
  5.   console.info('中断结束,可以恢复播放');
  6. });
复制代码

八、API 归纳与选型建议

Voice API:has.playVoice、has.pauseVoice、has.stopVoice,参数简单,适合本地语音和提示音。InnerAudioContext:属性包括 src、startTime、autoplay、loop、volume、playbackRate;方法包括 play、pause、stop、seek、destroy;事件包括 onCanplay、onPlay、onPause、onStop、onEnded、onTimeUpdate、onError、onSeeked、onSeeking、onWaiting,并都有对应 off。AudioContext:has.createAudioContext 后只有 setSrc、play、pause、seek。

最后补充一个细节:InnerAudioContext 的 currentTime 可读可写,直接赋值就能跳转,效果和调 seek 一样。也就是说,this.audioCtx.currentTime = 10 等价于 this.audioCtx.seek(10)。

总结下来,简单场景用 Voice API,需要精细控制用 InnerAudioContext;创建实例要 destroy,Voice API 不支持网络路径,pause 和 stop 的区别要分清。把这几个点处理好,鸿蒙 ASCF 音频播放大体就稳了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-22 16:01 , Processed in 0.023028 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部