You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用expo-av检测React Native中音频播放结束?

问题分析与解决方案

你的问题出在重复触发播放操作以及音频资源清理逻辑不严谨,导致didJustFinish状态无法正确触发。

核心问题点

  1. 你在Audio.Sound.createAsync中已经设置了shouldPlay: true,这会让音频创建完成后自动开始播放,但随后又调用了sound.playAsync(),这会干扰播放状态的监听流程,导致结束事件无法被捕获。
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:52:12