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

react-native-track-player v3.2.0无效音频链接Play()不抛错的解决咨询

react-native-track-player 3.2.0 无效音频链接超时处理方案

问题背景

使用react-native-track-player": "^3.2.0"开发音频播放功能,核心播放逻辑如下:

async function setUpNewTrack() {
    try {
      await TrackPlayer.reset()
      await TrackPlayer.add({
        id: podcastEpisode._id,
        url: podcastEpisode.attributes.audio,
        title: podcastEpisode.attributes.title,
        artist: podcastEpisode.attributes.author,
        artwork: podcastEpisode.attributes.image || podcastEpisode.relationships.podcast.data.image,
        description: podcastEpisode.attributes.description || podcastEpisode.attributes.content,
      })
      setCurrentPlayingPodcast(podcastEpisode)

      if (podcastPlaybackTime !== 0) {
        await TrackPlayer.seekTo(podcastPlaybackTime)
      }
      await TrackPlayer.play()
      setCurrentPlayingPodcastId(podcastEpisode._id)
      setCurrentlyPlayPodcast(podcastEpisode._id)
    } catch (error) {
      Alert.alert("Error", "There was an error playing the podcast")
      console.error({
        message: "Error in PodcastMediaPlayer.tsx: setUpNewTrack",
        error,
      })
    }
  }

当音频链接podcastEpisode.attributes.audio无效时,调用TrackPlayer.play()后播放状态会一直停留在connecting,但函数不会抛出任何错误,无法触发错误提示逻辑。

已尝试的无效方案

  1. Promise.race 结合超时
    由于TrackPlayer.play()仅触发播放动作,会立即resolve,导致超时Promise永远不会被触发,无法生效:

    const playerLoadingTimeout = new Promise(
            (_resolve, reject) =>
              setTimeout(() => reject(new Error("We couldn't load the podcast.")), 10000), // 10 seconds
          )
        await Promise.race([TrackPlayer.play(), playerLoadingTimeout])
          .then((res) => console.log(res))
          .catch((err) => Alert.alert("OHO !", err.message))
    
  2. 监听playbackState 动态设置超时
    每次playBackState变化都会重置超时定时器,若状态出现波动(如短暂切换),会导致超时逻辑被打断,无法准确检测长时间连接状态:

    function handleLongConnection() {
      const timeout = setTimeout(() => {
        try {
          if (playBackState === State.Connecting || playBackState === State.Buffering) {
            Alert.alert("Error", "There was an error playing the podcast")
            TrackPlayer.reset()
          }
        } catch (error) {
          console.log("THERE IS AN ERROR", error)
        }
      }, 10000)
    
      return () => clearTimeout(timeout)
    }
    
    useEffect(() => {
      return handleLongConnection()
    }, [playBackState])
    

可行解决方案

1. 单次超时+状态监听结合

在调用播放逻辑时,手动启动一个全局超时定时器,同时订阅播放状态变化事件,一旦状态变为Playing或Error,立即清除超时;若超时触发时仍处于Connecting/Buffering状态,则判定为加载失败,执行错误处理。

修改后的setUpNewTrack函数示例:

import { Subscription } from 'react-native-track-player';

async function setUpNewTrack() {
  let connectionTimeout: NodeJS.Timeout | null = null;
  let stateSubscription: Subscription | null = null;

  try {
    await TrackPlayer.reset();
    await TrackPlayer.add({
      id: podcastEpisode._id,
      url: podcastEpisode.attributes.audio,
      title: podcastEpisode.attributes.title,
      artist: podcastEpisode.attributes.author,
      artwork: podcastEpisode.attributes.image || podcastEpisode.relationships.podcast.data.image,
      description: podcastEpisode.attributes.description || podcastEpisode.attributes.content,
    });
    setCurrentPlayingPodcast(podcastEpisode);

    if (podcastPlaybackTime !== 0) {
      await TrackPlayer.seekTo(podcastPlaybackTime);
    }

    // 启动10秒超时定时器
    connectionTimeout = setTimeout(async () => {
      const currentState = await TrackPlayer.getState();
      if (currentState === State.Connecting || currentState === State.Buffering) {
        Alert.alert("Error", "Failed to load the podcast: connection timed out");
        await TrackPlayer.reset();
        // 重置本地状态
        setCurrentPlayingPodcastId(null);
        setCurrentlyPlayPodcast(null);
      }
    }, 10000);

    // 订阅播放状态变化,用于提前终止超时
    stateSubscription = TrackPlayer.addEventListener('playback-state', async (event) => {
      if (event.state === State.Playing || event.state === State.Error) {
        if (connectionTimeout) {
          clearTimeout(connectionTimeout);
          connectionTimeout = null;
        }
        if (stateSubscription) {
          stateSubscription.remove();
          stateSubscription = null;
        }
      }
    });

    await TrackPlayer.play();
    setCurrentPlayingPodcastId(podcastEpisode._id);
    setCurrentlyPlayPodcast(podcastEpisode._id);
  } catch (error) {
    Alert.alert("Error", "There was an error playing the podcast");
    console.error({
      message: "Error in PodcastMediaPlayer.tsx: setUpNewTrack",
      error,
    });
    // 清理定时器和订阅
    if (connectionTimeout) clearTimeout(connectionTimeout);
    if (stateSubscription) stateSubscription.remove();
  }
}

2. 兼容iOS/Android reset状态差异

针对iOS调用TrackPlayer.reset()后状态变为State.Pause、Android变为idle的问题,可在reset后手动同步本地状态,或在状态判断时做兼容处理:

import { Platform } from 'react-native';

// 调用reset后手动更新本地状态
await TrackPlayer.reset();
// 根据平台重置本地状态
if (Platform.OS === 'ios') {
  // iOS reset后状态为Pause,手动标记为None
  setPlayBackState(State.None);
} else {
  // Android reset后状态为Idle,对应State.None
  setPlayBackState(State.None);
}

或者在状态监听回调中,针对iOS的Pause状态做额外判断:

TrackPlayer.addEventListener('playback-state', async (event) => {
  let normalizedState = event.state;
  // iOS reset后会进入Pause,手动修正为None
  if (Platform.OS === 'ios' && event.state === State.Pause) {
    const currentQueue = await TrackPlayer.getQueue();
    if (currentQueue.length === 0) {
      normalizedState = State.None;
    }
  }
  setPlayBackState(normalizedState);
});

内容的提问来源于stack exchange,提问作者stehvanin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:54:53