在项目里有两处地方会创建 AudioRenderer:
-
A 页面使用 Promise 版:
audio.createAudioRenderer(options) -
B 页面使用 callback 版:
audio.createAudioRenderer(options, (err, data) => {...})
日常切换页面播放几次音频后,发现:
-
播放过程中偶尔出现“设备忙”或“渲染器创建失败”的异常;
-
真机上系统音频占用偏高,怀疑有渲染器没有正确
release()。
// A 页面
audio.createAudioRenderer(options)
.then((renderer) => {
this.renderer = renderer;
this.renderer.start();
});
// B 页面
audio.createAudioRenderer(options, (err, data) => {
if (!err) {
this.renderer = data;
this.renderer.start();
}
});
两个页面都只在结束时 this.renderer.stop(),没有统一管理。
只要你没有调用 release(),之前创建的 AudioRenderer 就可能一直占用系统资源。不论是 Promise 版还是 callback 版,底层对象是一样的。
建议做两件事:
-
统一封装成 Promise 风格(即使底层是 callback 版);
-
集中管理渲染器的生命周期,比如做一个单例
AudioRendererService。
示例封装:
// AudioRendererService.ets
import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class AudioRendererService {
private static instance: AudioRendererService;
private renderer?: audio.AudioRenderer;
static getInstance(): AudioRendererService {
if (!this.instance) {
this.instance = new AudioRendererService();
}
return this.instance;
}
async createRenderer(options: audio.AudioRendererOptions): Promise<audio.AudioRenderer> {
// 如果已有实例,先释放
if (this.renderer) {
await this.destroyRenderer();
}
this.renderer = await audio.createAudioRenderer(options);
return this.renderer;
}
async playPcmOnce(buffer: ArrayBuffer) {
if (!this.renderer) {
throw new Error('renderer is not created');
}
await this.renderer.start();
await this.renderer.write(buffer);
await this.renderer.stop();
}
async destroyRenderer() {
if (this.renderer) {
try {
await this.renderer.stop();
} catch (e) {
// 忽略已停止的错误
}
await this.renderer.release();
this.renderer = undefined;
}
}
}
页面使用时,不再直接调用 audio.createAudioRenderer,而是统一通过 service:
// 某个页面
const service = AudioRendererService.getInstance();
const renderer = await service.createRenderer(options);
// 播放
await service.playPcmOnce(buffer);
// 页面销毁时
await service.destroyRenderer();
好处:
-
Promise 和 callback 版本都可以在 service 里统一转换成 Promise(如果必须用 callback 版,可以手动
new Promise封装); -
所有渲染器都通过同一个入口创建和销毁,不会出现 A 页面释放了自己的 renderer,但 B 页面还握着另一个 renderer 没 release 的情况;
-
避免频繁创建多个并行
AudioRenderer导致“设备忙”、“资源不足”等问题。
因此,问题的关键不是用 Promise 版还是 callback 版,而是要保证每次创建对应一次完整的 stop + release,并且集中管理整个应用中的渲染器数量。