鸿蒙ArkTS实战:手把手教你封装一个可复用的音乐播放器管理类(含完整代码)
鸿蒙ArkTS实战构建高内聚低耦合的音乐播放器管理类在鸿蒙应用开发中音频播放功能是许多应用的核心需求。传统的实现方式往往将播放逻辑直接嵌入UI页面导致代码臃肿、难以维护。本文将展示如何通过ArkTS设计一个高度可复用的AvPlayerManager服务类实现播放逻辑与UI的彻底解耦。1. 播放器管理类的架构设计优秀的架构设计应该遵循单一职责原则。我们的AvPlayerManager需要承担以下核心职责管理AVPlayer实例的生命周期处理音频源切换和播放状态控制封装播放状态变更的事件通知提供跨页面的播放信息同步能力// 定义歌曲数据接口 export interface SongItem { img: string; name: string; author: string; url: string; id: string; } // 播放状态枚举 enum PlaybackState { IDLE, INITIALIZED, PREPARED, PLAYING, PAUSED }关键设计决策使用静态类避免重复实例化通过事件机制实现状态通知异步方法确保线程安全类型化的接口定义提升代码可靠性2. 核心功能实现详解2.1 播放器初始化与状态管理播放器的初始化需要考虑资源释放和异常处理export class AvPlayerManager { private static avPlayer: media.AVPlayer | null null; private static currentState: PlaybackState PlaybackState.IDLE; static async initialize(): Promisevoid { try { if (this.avPlayer) { await this.release(); } this.avPlayer await media.createAVPlayer(); this.setupEventListeners(); this.currentState PlaybackState.INITIALIZED; } catch (error) { console.error(播放器初始化失败:, error); throw new Error(播放器初始化失败); } } private static setupEventListeners(): void { this.avPlayer?.on(stateChange, (state) { switch(state) { case initialized: this.currentState PlaybackState.INITIALIZED; this.avPlayer?.prepare(); break; case prepared: this.currentState PlaybackState.PREPARED; break; // 其他状态处理... } }); } }注意每次应用启动时应只初始化一个AVPlayer实例避免资源浪费2.2 播放控制方法封装我们封装了完整的播放控制方法集static async play(song: SongItem): Promisevoid { if (!this.avPlayer) { await this.initialize(); } try { await this.avPlayer?.reset(); this.avPlayer!.url song.url; await this.avPlayer?.prepare(); await this.avPlayer?.play(); this.currentState PlaybackState.PLAYING; this.notifyPlaybackStart(song); } catch (error) { console.error(播放失败:, error); throw new Error(无法播放歌曲: ${song.name}); } } static async togglePlayback(): Promisevoid { if (this.currentState PlaybackState.PLAYING) { await this.avPlayer?.pause(); this.currentState PlaybackState.PAUSED; } else if (this.currentState PlaybackState.PAUSED) { await this.avPlayer?.play(); this.currentState PlaybackState.PLAYING; } }功能对比表方法名参数返回值功能描述playSongItemPromise播放指定歌曲pause无Promise暂停当前播放resume无Promise恢复播放stop无Promise停止并释放资源3. 跨页面状态同步方案在多页面应用中保持播放状态一致是个挑战。我们采用事件总线状态持久化的方案import emitter from ohos.events.emitter; // 定义事件类型 enum PlayerEvents { PLAYBACK_START 1001, PLAYBACK_PAUSE 1002, PLAYBACK_PROGRESS 1003 } static notifyPlaybackStart(song: SongItem): void { emitter.emit({ eventId: PlayerEvents.PLAYBACK_START }, { data: { song: JSON.stringify(song), timestamp: new Date().getTime() } }); } // 在页面中监听 emitter.on({ eventId: PlayerEvents.PLAYBACK_START }, (eventData) { const song JSON.parse(eventData.data.song) as SongItem; // 更新UI状态 });状态同步流程图用户在主页面点击播放AvPlayerManager处理播放逻辑通过emitter发送播放事件各个页面监听并更新自己的UI保持整个应用的播放状态一致4. 高级功能扩展4.1 播放队列管理实现播放列表和队列功能private static playQueue: SongItem[] []; private static currentIndex: number -1; static addToQueue(songs: SongItem | SongItem[]): void { if (Array.isArray(songs)) { this.playQueue.push(...songs); } else { this.playQueue.push(songs); } } static async playNext(): Promisevoid { if (this.currentIndex this.playQueue.length - 1) { this.currentIndex; await this.play(this.playQueue[this.currentIndex]); } } static async playPrevious(): Promisevoid { if (this.currentIndex 0) { this.currentIndex--; await this.play(this.playQueue[this.currentIndex]); } }4.2 播放状态持久化使用Preferences实现播放状态保存import preferences from ohos.data.preferences; static async savePlaybackState(): Promisevoid { const prefs await preferences.getPreferences(player_state); await prefs.put(lastPlayedSong, JSON.stringify(this.playQueue[this.currentIndex])); await prefs.put(playbackPosition, this.avPlayer?.currentTime || 0); await prefs.flush(); } static async restorePlaybackState(): Promisevoid { const prefs await preferences.getPreferences(player_state); const lastSong await prefs.get(lastPlayedSong, ); if (lastSong) { const song JSON.parse(lastSong.toString()) as SongItem; await this.play(song); const position await prefs.get(playbackPosition, 0); this.avPlayer?.seek(position as number); } }5. 性能优化与异常处理5.1 内存管理最佳实践static async release(): Promisevoid { if (this.avPlayer) { await this.savePlaybackState(); this.avPlayer.off(stateChange); await this.avPlayer.release(); this.avPlayer null; this.currentState PlaybackState.IDLE; } } // 在页面生命周期中调用 aboutToDisappear(): void { if (!keepPlayerAlive) { AvPlayerManager.release(); } }5.2 健壮性增强措施static async safePlay(song: SongItem, retries 3): Promisevoid { for (let i 0; i retries; i) { try { await this.play(song); return; } catch (error) { if (i retries - 1) throw error; await new Promise(resolve setTimeout(resolve, 1000)); } } } // 网络音频源处理 static async playRemoteAudio(url: string): Promisevoid { try { const response await fetch(url); if (!response.ok) throw new Error(网络请求失败); const tempPath getTempFilePath(); await saveAudioToFile(response.body, tempPath); await this.play({ url: tempPath } as SongItem); } catch (error) { console.error(播放远程音频失败:, error); throw error; } }在实际项目中这种架构设计使得播放功能模块可以独立测试和演进UI层只需关注展示逻辑。通过完整的状态管理和事件通知机制不同页面间的播放体验能够保持高度一致。