讨论广场 问答详情
在 Harmony 开发中同时使用回调版和 Promise 版 createAudioRenderer 会造成资源没释放吗?如何规范封装?
李游Leo 2025-11-17 10:00:11
63 评论 分享

在项目里有两处地方会创建 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(),没有统一管理。

63 评论 分享
写回答
全部评论(1)

只要你没有调用 release(),之前创建的 AudioRenderer 就可能一直占用系统资源。不论是 Promise 版还是 callback 版,底层对象是一样的。

建议做两件事:

  1. 统一封装成 Promise 风格(即使底层是 callback 版);

  2. 集中管理渲染器的生命周期,比如做一个单例 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,并且集中管理整个应用中的渲染器数量

2025-11-17 10:47:30