如何用expo-av检测React Native中音频播放结束?
问题分析与解决方案
你的问题出在重复触发播放操作以及音频资源清理逻辑不严谨,导致didJustFinish状态无法正确触发。
核心问题点
- 你在
Audio.Sound.createAsync中已经设置了shouldPlay: true,这会让音频创建完成后自动开始播放,但随后又调用了sound.playAsync(),这会干扰播放状态的监听流程,导致结束事件无法被捕获。 - 在
FINISH_AUDIO中直接将currentSound置为null,没有先完成音频资源的卸载,可能导致状态监听的回调被提前终止。
修正后的代码
1. 调整PLAY函数,移除重复的playAsync调用
const [currentSound, setCurrentSound] = useState<Audio.Sound | null>(null) const [playingIndex, setPlayingIndex] = useState<number | null>(null) const [isPlaying, setIsPlaying] = useState<boolean>(false) const PLAY = async(uri: string, index: number) => { // 先清理之前的音频资源 if(currentSound) { await currentSound.stopAsync() await currentSound.unloadAsync() setCurrentSound(null) // 清理引用 } if(uri) { await Audio.setAudioModeAsync({ playsInSilentModeIOS: true }) // createAsync的shouldPlay: true已经会自动播放,无需再调用playAsync const { sound } = await Audio.Sound.createAsync( { uri }, { shouldPlay: true }, FINISH_AUDIO ) setCurrentSound(sound) setPlayingIndex(index) setIsPlaying(true) } else { console.error('No sound URI found in AsyncStorage'); } }
2. 完善FINISH_AUDIO的资源清理逻辑
const FINISH_AUDIO = async(status: AVPlaybackStatus) => { console.log(status) if(status.isLoaded && status.didJustFinish) { // 确保音频确实播放完毕,再更新状态并清理资源 setIsPlaying(false) setPlayingIndex(null) // 先卸载音频资源,再置空引用 if(currentSound) { await currentSound.unloadAsync() setCurrentSound(null) } } }
额外注意事项
- 确保你的expo-av版本是最新的,旧版本可能存在播放状态监听的bug。
- 如果是iOS设备,检查
Audio.setAudioModeAsync的其他配置是否影响播放(比如allowsRecordingIOS等),避免冲突。
内容的提问来源于stack exchange,提问作者Edwin Aquino
相关产品推荐
相关产品推荐

