HarmonyOS Interface (AVPlayer) 使用指南:从入门到实战

摘要: AVPlayer 是 HarmonyOS 媒体播放的核心接口,支持音频和视频的端到端播放。本文基于 HarmonyOS NEXT开发实践,系统讲解 AVPlayer 的状态机模型、完整播放流程、资源加载方式和控制方法,帮助开发者快速实现音频播放功能。


效果

一、AVPlayer 概述

AVPlayer 位于 @kit.MediaKit 模块的 media 命名空间下,是 HarmonyOS 推荐的媒体播放组件。相比旧版 PlayerAVPlayer 提供了更完善的状态机管理和更丰富的功能支持。

1.1 导入方式

import { media } from '@kit.MediaKit';

1.2 支持的播放源

播放源类型 说明 设置方式
fdSrc 本地文件描述符(rawfile资源) avPlayer.fdSrc = { fd, offset, length }
url 网络URL地址 avPlayer.url = 'https://...'
dataSrc 内存数据流 avPlayer.dataSrc = dataSrcDescriptor

二、状态机模型

AVPlayer 采用有限状态机设计,所有操作必须在正确的状态下调用。理解状态机是使用 AVPlayer 的关键。

2.1 状态流转图

                    ┌──────────────────────────────────────────────┐
                    │                                              │
                    ▼                                              │
 [idle] ──setPlaySource──▶ [initialized] ──prepare──▶ [prepared]  │
   ▲                                                       │      │
   │                                                       ▼      │
   │  reset()    [released] ◄──release()── [stopped] ◄──play()──▶[playing]
   │                    ▲                            ▲            │
   │                    │                            │            ▼
   │                    │                       stop()      [paused]
   │                    │                            │            │
   │                    │                            │       play()│
   │                    │                            │            │
   │                    │                            ▼            │
   │                    │                       [completed]───────┘
   │                    │                            │
   │                    │                            │ (循环播放)
   │                    │                            ▼
   │                    └────────────────────── [prepared]
   │
   └── error ──▶ [error] ──reset()──▶ [idle]

2.2 各状态说明

状态 触发条件 可执行操作
idle 初始状态 / reset后 / error后 release()
initialized 设置播放源后(fdSrc/url) prepare()
prepared prepare成功后 play()seek()setPlaybackSpeed()
playing play成功后 pause()stop()seek()
paused pause成功后 play()stop()seek()
completed 播放结束 stop()seek()
stopped stop后 prepare()(循环播放)
released release后 不可再操作,实例已销毁
error 操作出错 reset() 回到 idle

三、完整播放流程

3.1 创建 AVPlayer 实例

let avPlayer: media.AVPlayer = await media.createAVPlayer();

3.2 注册回调函数

必须在设置播放源之前注册回调,否则可能错过状态变化通知。

// 状态机变化回调(最核心的回调)
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
  switch (state) {
    case 'idle':
      // idle状态:reset后触发,可release释放资源
      avPlayer.release();
      break;
    case 'initialized':
      // initialized状态:设置播放源后触发
      avPlayer.prepare();  // 准备播放
      break;
    case 'prepared':
      // prepared状态:准备完成,可以播放
      avPlayer.play();     // 开始播放
      break;
    case 'playing':
      // 正在播放
      break;
    case 'paused':
      // 已暂停
      break;
    case 'completed':
      // 播放完成
      avPlayer.stop();     // 停止播放
      break;
    case 'stopped':
      // 已停止,可重新prepare循环播放
      avPlayer.prepare();
      break;
    case 'released':
      // 已释放资源
      break;
  }
});

// seek操作完成回调
avPlayer.on('seekDone', (seekDoneTime: number) => {
  console.info(`seek完成,当前位置: ${seekDoneTime}ms`);
});

// 错误回调
avPlayer.on('error', (err) => {
  console.error(`播放错误: code=${err.code}, message=${err.message}`);
  avPlayer.reset();  // 出错后调用reset回到idle状态
});

3.3 设置播放源

方式一:播放 rawfile 资源(推荐用于本地音频)
import { common } from '@kit.AbilityKit';

async function playRawfileAudio(ctx: Context) {
  let avPlayer = await media.createAVPlayer();

  // 注册回调(省略,见3.2)
  registerCallbacks(avPlayer);

  // 获取rawfile资源
  let context = ctx as common.UIAbilityContext;
  let fileDescriptor = await context.resourceManager.getRawFd('audio.mp3');
  let avFileDescriptor: media.AVFileDescriptor = {
    fd: fileDescriptor.fd,
    offset: fileDescriptor.offset,
    length: fileDescriptor.length
  };

  // 设置播放源 → 触发 initialized 状态
  avPlayer.fdSrc = avFileDescriptor;
}
方式二:播放网络URL
async function playUrlAudio() {
  let avPlayer = await media.createAVPlayer();

  // 注册回调(省略,见3.2)
  registerCallbacks(avPlayer);

  // 设置URL → 触发 initialized 状态
  avPlayer.url = 'https://example.com/audio.mp3';
}

3.4 播放控制

// 播放
avPlayer.play();

// 暂停
avPlayer.pause();

// 停止
avPlayer.stop();

// 跳转(单位:毫秒)
avPlayer.seek(30000);  // 跳转到30秒

// 释放资源
avPlayer.release();

// 重置(回到idle状态)
avPlayer.reset();

四、完整示例:音频播放服务

以下示例封装了一个完整的音频播放服务类,支持播放/暂停切换、错误处理和资源管理。

4.1 音频播放服务类

import { media } from '@kit.MediaKit';
import { common } from '@kit.AbilityKit';
import { audio } from '@kit.AudioKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

export class AudioPlayerService {
  private avPlayer?: media.AVPlayer = undefined;
  private currentState: string = '';
  public static instance: AudioPlayerService = new AudioPlayerService();

  /**
   * 注册AVPlayer回调函数
   */
  private registerCallbacks(avPlayer: media.AVPlayer): void {
    // 状态机变化回调
    avPlayer.on('stateChange', async (state: string) => {
      hilog.info(0x0000, 'AudioPlayer', '状态变化: %{public}s', state);
      this.currentState = state;

      switch (state) {
        case 'idle':
          // reset后到达idle,释放资源
          avPlayer.release();
          break;
        case 'initialized':
          // 设置播放源后到达,配置音频参数并准备
          avPlayer.audioRendererInfo = {
            usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
            rendererFlags: 0
          };
          avPlayer.prepare();
          break;
        case 'prepared':
          // 准备完成,开始播放
          avPlayer.play();
          break;
        case 'playing':
          hilog.info(0x0000, 'AudioPlayer', '正在播放');
          break;
        case 'paused':
          hilog.info(0x0000, 'AudioPlayer', '已暂停');
          break;
        case 'completed':
          // 播放完成,停止后可重新prepare实现循环
          avPlayer.stop();
          break;
        case 'stopped':
          // 停止后重新准备,实现循环播放
          avPlayer.prepare();
          break;
        case 'released':
          hilog.info(0x0000, 'AudioPlayer', '资源已释放');
          break;
      }
    });

    // seek完成回调
    avPlayer.on('seekDone', (seekDoneTime: number) => {
      hilog.info(0x0000, 'AudioPlayer', 'seek完成: %{public}d', seekDoneTime);
    });

    // 错误回调
    avPlayer.on('error', (err) => {
      hilog.error(0x0000, 'AudioPlayer',
        '播放错误: code=%{public}d, message=%{public}s', err.code, err.message);
      avPlayer.reset();
    });
  }

  /**
   * 播放rawfile中的音频资源
   */
  async playFromRawfile(ctx: Context, resourceName: string): Promise<void> {
    let avPlayer = await media.createAVPlayer();
    this.registerCallbacks(avPlayer);

    let context = ctx as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(resourceName);
    let avFileDescriptor: media.AVFileDescriptor = {
      fd: fileDescriptor.fd,
      offset: fileDescriptor.offset,
      length: fileDescriptor.length
    };
    avPlayer.fdSrc = avFileDescriptor;
    this.avPlayer = avPlayer;
  }

  /**
   * 切换播放/暂停
   */
  togglePlayPause(ctx: Context, resourceName: string): void {
    if (this.avPlayer === undefined) {
      // 首次播放,创建播放器
      this.playFromRawfile(ctx, resourceName);
    } else if (this.currentState === 'playing') {
      this.avPlayer.pause();
    } else {
      this.avPlayer.play();
    }
  }

  /**
   * 获取当前状态
   */
  getState(): string {
    return this.currentState;
  }
}

4.2 在组件中使用

import { AudioPlayerService } from '../utils/AudioPlayerService';

@Entry
@Component
struct AudioPage {
  @Local isPlaying: boolean = false;
  private ctx?: Context = this.getUIContext().getHostContext();

  build() {
    Column({ space: 20 }) {
      Text('AVPlayer 音频播放示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Text(this.isPlaying ? '正在播放...' : '已暂停')
        .fontSize(16)
        .fontColor(this.isPlaying ? '#4CAF50' : '#999999')

      Button(this.isPlaying ? '暂停' : '播放')
        .fontSize(16)
        .onClick(() => {
          AudioPlayerService.instance.togglePlayPause(
            this.ctx!, 'sample.wav');
          this.isPlaying = !this.isPlaying;
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

五、音频渲染参数配置

5.1 audioRendererInfo

initialized 状态下设置音频渲染参数:

avPlayer.audioRendererInfo = {
  // 音频流使用类型
  usage: audio.StreamUsage.STREAM_USAGE_MUSIC,  // 音乐
  // rendererFlags: 0  // 音频渲染器标志
};

常用 StreamUsage 类型:

类型 说明
STREAM_USAGE_MUSIC 音乐播放
STREAM_USAGE_VOICE_COMMUNICATION 语音通话
STREAM_USAGE_NOTIFICATION 通知提示音
STREAM_USAGE_ALARM 闹钟
STREAM_USAGE_GAME 游戏音效

5.2 其他播放参数

// 设置音量(0.0 ~ 1.0)
avPlayer.volume = 0.8;

// 设置播放速度(0.5x ~ 2.0x)
avPlayer.setPlaybackSpeed(1.5);

// 设置循环模式(需要手动在completed状态处理)
// AVPlayer本身不直接支持循环,需在stateChange回调中实现

六、开发要点与最佳实践

6.1 回调注册时机

正确顺序:
1. createAVPlayer()     → 创建实例
2. on('stateChange')    → 注册状态回调 ★ 必须在设置播放源之前
3. on('error')          → 注册错误回调 ★ 必须在设置播放源之前
4. fdSrc / url          → 设置播放源(触发状态机流转)

错误顺序:
1. createAVPlayer()
2. fdSrc                → ✗ 可能错过 initialized 状态
3. on('stateChange')    → ✗ 已来不及接收状态变化

6.2 错误处理策略

avPlayer.on('error', (err) => {
  // 1. 记录错误日志
  hilog.error(0x0000, 'AVPlayer', '错误: %{public}s', JSON.stringify(err));

  // 2. 调用reset回到idle状态
  avPlayer.reset();

  // 3. 在idle状态的stateChange回调中release释放资源
});

6.3 资源释放

// 正确的资源释放流程
avPlayer.stop();     // 先停止
// → stopped 状态
// 在 stateChange 的 idle 回调中:
avPlayer.release();  // 释放资源
// → released 状态,实例不可再使用

6.4 循环播放实现

AVPlayer 不直接支持循环播放,需要在状态机回调中手动实现:

avPlayer.on('stateChange', async (state: string) => {
  if (state === 'completed') {
    avPlayer.stop();     // 播放完成后停止
    // → stopped 状态
  }
  if (state === 'stopped') {
    avPlayer.prepare();  // 停止后重新准备
    // → prepared 状态 → play() 自动播放
  }
});

6.5 单例模式管理

推荐使用单例模式管理 AVPlayer 实例,避免创建多个播放器:

export class AudioPlayerService {
  public static instance: AudioPlayerService = new AudioPlayerService();
  private avPlayer?: media.AVPlayer;

  // 确保全局只有一个播放器实例
}

七、AVPlayer 回调事件速查表

事件名 说明 回调参数
stateChange 状态机变化 (state: string, reason: StateChangeReason)
seekDone seek操作完成 (seekDoneTime: number)
error 操作出错 (err: BusinessError)
timeUpdate 播放时间更新 (time: number)
durationUpdate 总时长更新 (duration: number)
bufferingUpdate 缓冲更新 (infoType: BufferingInfoType, value: number)
audioInterruptBegin 音频焦点被抢占开始 ()
audioInterruptEnd 音频焦点恢复 ()

八、总结

AVPlayer 是 HarmonyOS 媒体播放的核心组件,掌握以下要点即可应对大部分音频播放场景:

  1. 状态机模型:理解 idle → initialized → prepared → playing → paused → completed → stopped 的完整流转
  2. 回调注册:必须在设置播放源之前注册 stateChangeerror 回调
  3. 播放源设置:支持 fdSrc(本地资源)、url(网络地址)、dataSrc(内存数据)
  4. 播放控制:play、pause、stop、seek、release、reset
  5. 错误处理:error 状态下调用 reset 回到 idle,再 release 释放资源
  6. 循环播放:在 completed → stop → prepare 链路中实现

相关文档:

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐