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,但函数不会抛出任何错误,无法触发错误提示逻辑。
已尝试的无效方案
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))监听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
相关产品推荐
相关产品推荐

