使用Expo Audio API遇类型转换错误问题求助
问题:Expo Audio API切换音频/重进页面报错
在已执行eject操作的React Native项目中使用新版Expo Audio API,首次进入页面播放音频时无报错,但切换音频或重新进入该页面时触发错误:Error: The 1st argument cannot be cast to type expo.modules.audio.AudioPlayer (received class java.lang.Integer)
代码片段
function LoadedPlayer({ book }: { book: book }) { const bookProgress = useAppSelector( (state) => state.books.bookProgress[book.id || ""] ); let audioSource = { uri: getAudioURL(book?.chapters[0].audio_id), }; let AudioPlayer = useAudioPlayer(audioSource, 250); const AudioPlayerStatus = useAudioPlayerStatus(AudioPlayer); const sliderTouchableRef = useRef<View>(null); const loadChapterToTrack = (chapterNumber: number) => { if (!book.chapters || !book.chapters[chapterNumber] || !AudioPlayer) { return; } try { AudioPlayer.replace({ uri: getAudioURL(book.chapters[chapterNumber].audio_id), }); } catch (e) {} dispatch( updateBookProgress({ bookId: book.id || "", currentChapter: chapterNumber, currentProgressSeconds: 0, }) ); }; useEffect(() => { setAudioModeAsync({ shouldPlayInBackground: true, }); if (!bookProgress) { dispatch( updateBookProgress({ bookId: book.id || "", currentChapter: 0, currentProgressSeconds: 0, }) ); } (async () => { if (!book.chapters) { console.error("No chapters found for this book."); return; } // Start playing it AudioPlayer.play(); if (bookProgress?.currentProgressSeconds) { AudioPlayer.seekTo(bookProgress.currentProgressSeconds); } })(); setTimeout(() => { dispatch(addToCurrentlyReading(book.id || "")); }, 5000); const progressInterval = setInterval(() => { if (AudioPlayer.currentTime && AudioPlayer.duration) { dispatch( updateBookProgress({ bookId: book.id || "", currentChapter: bookProgress?.currentChapter || 0, currentProgressSeconds: AudioPlayer.currentTime, }) ); } }, 5000); return () => { clearInterval(progressInterval); AudioPlayer.release(); }; }, []); useEffect(() => { if (AudioPlayerStatus?.playbackState === "ended") { console.log("ended"); loadChapterToTrack(bookProgress?.currentChapter + 1 || 0); } }, [AudioPlayerStatus.playbackState]); return ( <ScreenContents /> ); }
问题原因
- 闭包 stale 引用:第一个
useEffect依赖数组为空,内部捕获的AudioPlayer、bookProgress都是组件初始渲染时的旧值。当组件重新渲染(切换章节、重进页面),新的AudioPlayer实例生成,但旧的useEffect回调仍在使用已被release的旧实例,调用replace或play时触发类型转换错误。 - 播放器生命周期管理不当:组件卸载时调用
AudioPlayer.release()后,仍有代码(如loadChapterToTrack、定时器)尝试访问已释放的实例,此时实例内部状态失效,被转换为Integer类型标识,导致参数类型不匹配。
解决方案
1. 修复useEffect依赖数组
将useEffect中用到的所有外部变量加入依赖数组,确保回调能获取最新的实例和状态:
useEffect(() => { // ... 原有逻辑 }, [AudioPlayer, bookProgress, book.chapters, book.id]);
2. 用Ref保存AudioPlayer实例
避免闭包中的 stale 引用,将AudioPlayer存入ref,确保后续操作访问最新实例:
const audioPlayerRef = useRef<ReturnType<typeof useAudioPlayer> | null>(null); audioPlayerRef.current = useAudioPlayer(audioSource, 250); const AudioPlayerStatus = useAudioPlayerStatus(audioPlayerRef.current); // 后续操作改为使用audioPlayerRef.current const loadChapterToTrack = (chapterNumber: number) => { const player = audioPlayerRef.current; if (!book.chapters || !book.chapters[chapterNumber] || !player) { return; } try { player.replace({ uri: getAudioURL(book.chapters[chapterNumber].audio_id), }); } catch (e) {} // ... 其他逻辑 };
3. 完整清理定时器与播放器
确保组件卸载时清理所有定时器,避免内存泄漏和无效实例访问:
useEffect(() => { const addReadingTimeout = setTimeout(() => { dispatch(addToCurrentlyReading(book.id || "")); }, 5000); const progressInterval = setInterval(() => { const player = audioPlayerRef.current; if (player?.currentTime && player.duration) { dispatch( updateBookProgress({ bookId: book.id || "", currentChapter: bookProgress?.currentChapter || 0, currentProgressSeconds: player.currentTime, }) ); } }, 5000); return () => { clearInterval(progressInterval); clearTimeout(addReadingTimeout); audioPlayerRef.current?.release(); }; }, [audioPlayerRef.current, bookProgress, book.id]);
4. 前置检查播放器有效性
在调用播放器方法前,先确认实例是否存在且可用:
(async () => { const player = audioPlayerRef.current; if (!book.chapters || !player) { console.error("No chapters found or invalid audio player."); return; } player.play(); if (bookProgress?.currentProgressSeconds) { player.seekTo(bookProgress.currentProgressSeconds); } })();
内容的提问来源于stack exchange,提问作者10xNerd
相关产品推荐
相关产品推荐

