书接上回:我们在v1.0.7版本中实现了多条音乐的自动顺序播放,但是顺序播放对流程的控制和倒计时发生了冲突:每次切换到下一首歌,倒计时都会重新开始计时;但在单曲循环模式下却一切正常。设想一下,我们本想设定好30分钟睡眠定时,结果每换一首歌就和月光宝盒一样时光倒流,重新开始计时,这不是永远睡不着了!💤一、“犯罪现场” 寻找虫子活动的蛛丝马迹
首先看下出问题的顺序播放的代码:
// 在顺序播放完成时else if (this.playMode === PlayMode.SEQUENTIAL) { console.info('执行顺序播放,尝试播放下一首'); if (this.currentAudioIndex < this.audioList.length - 1) { const nextIndex = this.currentAudioIndex + 1; this.updateStatus(`顺序播放,下一首: ${this.audioList[nextIndex].name}`); this.playAudioByIndex(nextIndex); // 这里调用了播放下一首 } // ...}
这里并没有重新开始倒计时的动作。
我们再看看播放函数:
async playAudioByIndex(index: number) { // ... 一些检查代码 await this.playAudioFile(audioFile.fileName, audioFile.name);}async playAudioFile(fileName: string, audioName: string) { // ... 准备播放器的代码 this.startCountdown(); // 倒计时每次都从这里开始!}
真相水落石出:每次在调用playAudioFile()函数时,都会执行startCountdown()函数,重启倒计时。
那么问题来了,为什么单曲循环模式,不会重启倒计时呢?还是得看代码:
if (this.playMode === PlayMode.SINGLE_LOOP) { // 注意这里!单曲循环是直接操作当前播放器 this.avPlayer!.seek(0); this.avPlayer!.play(); // 没有调用 playAudioByIndex,所以倒计时不会重启}
可以看出来,单曲循环模式是对当前播放器进行操作,并没有重建一个播放器实例,而是把当前播放器的播放进度重置为0,然后开始播放。没有调用播放函数,因此也就没有重启倒计时。
关键线索:
playAudioByIndex() 方法每次都会重新创建播放器,并无条件地调用startCountdown(),导致倒计时被重置。
二、解决方案:是否启动倒计时加个“开关”
区分场景:
当用户主动操作 → 重置倒计时;当顺序播放自动切换时 → 不重置倒计时。
具体修改点:
在playAudioByIndex()和playAudioFile()增加参数,根据参数判断是否重启倒计时。
// v1.0.8 修改:增加参数控制是否重置倒计时async playAudioByIndex(index: number, resetCountdown: boolean = true) { if (index < 0 || index >= this.audioList.length) { this.updateStatus('音频索引无效'); return; } const audioFile = this.audioList[index]; this.currentAudioIndex = index; await this.playAudioFile(audioFile.fileName, audioFile.name, resetCountdown);}// v1.0.8 修改:增加resetCountdown参数async playAudioFile(fileName: string, audioName: string, resetCountdown: boolean = true) { try { // 释放之前的播放器 await this.releasePlayer(); // 更新状态 this.updateStatus(`正在加载: ${audioName}...`); this.playerState = PlayerState.PREPARING; // 创建AVPlayer实例 this.avPlayer = await media.createAVPlayer(); // 设置事件监听 this.setAVPlayerCallbacks(); // 获取ResourceManager并打开rawfile资源 let resourceMgr = this.context.resourceManager; let fileDescriptor = await resourceMgr.getRawFd(fileName); // 设置音频源 this.avPlayer.fdSrc = { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length }; console.info(`音频源设置成功: ${audioName}`); // v1.0.8 修改:根据参数决定是否启动倒计时 if (resetCountdown) { this.startCountdown(); } } catch (error) { console.error('播放音频出错:', error); this.updateStatus(`播放失败: ${error.message}`); this.playerState = PlayerState.ERROR; this.clearCountdown(); }}
在顺序播放模式处增加参数,值为false,表示不重置倒计时:
// v1.0.8 修改:传递false,表示不重置倒计时 this.playAudioByIndex(nextIndex, false);
在点击列表某条音乐,以及点击上一首,下一首按钮,手动切换音乐时,重启倒计时:
// 用户主动点击列表时,应该重置倒计时.onClick(() => { this.currentAudioIndex = index; // 用户主动点击时重置倒计时 this.playAudioByIndex(index, true);})// 上一首/下一首按钮async playPrev() { if (this.audioList.length === 0) return; let prevIndex = this.currentAudioIndex - 1; if (prevIndex < 0) { prevIndex = this.audioList.length - 1; } // 用户主动切换时重置倒计时 await this.playAudioByIndex(prevIndex, true);}async playNext() { if (this.audioList.length === 0) return; let nextIndex = this.currentAudioIndex + 1; if (nextIndex >= this.audioList.length) { nextIndex = 0; } // 用户主动切换时重置倒计时 await this.playAudioByIndex(nextIndex, true);}
3. 修改后的使用场景对比表:
大功告成:让我们安心的欣赏音乐吧。
播放器完整代码可在评论区留言“1”获取!~
预计下个版本(v1.0.9)将实现随机播放,欲知后事如何,且听下回~分~解!