Expo Audio在iOS端Chrome和Safari(真机)播放连续音频片段的异常问题及解决方案咨询
Expo Audio在iOS端Chrome和Safari(真机)播放连续音频片段的异常问题及解决方案咨询
大家好,我最近基于Expo Audio做了一个按列表播放音频片段的功能,碰到了一个只在iOS真机上出现的诡异问题,想请教下有没有同学遇到过类似情况,或者有什么解决思路?
问题背景
这个功能是接收一组音频URL的列表,按顺序播放,列表里的单元格可能是音频段或者暂停段。在除了iOS Chrome和Safari(真机)之外的所有浏览器里都运行流畅,甚至iOS模拟器上的Safari和Chrome iOS都能正常工作,但iOS真机的Safari就是不行。
具体诡异现象
- 用户第一次点击播放后,第一个音频单元格能正常播放
- 当从第一个单元格切换到第二个单元格(不管是什么类型,中间一定会经过一个暂停单元格)时,从日志能看到代码已经触发了所有播放逻辑,但音频就是完全不播放
- 只要用户手动点一下“下一曲”按钮,或者切换一次播放/暂停状态,之后所有的播放就都正常了,再也不会出问题
之前还遇到过Safari iOS的权限拒绝错误,后来调整了暂停的异步逻辑后,Chrome iOS的问题解决了,但Safari真机的问题依然存在——它就像没识别到第一次的播放交互一样,完全不响应后续的自动播放触发。
已尝试的临时方案&调整
- 目前临时的解决办法是在第一次切换失败时显示“点击继续”按钮,用户点一次后就正常了,但这样用户需要点两次才能进入正常播放流程,体验不太好
- 调整Chrome iOS的暂停逻辑:把原本不必要的异步暂停函数改成同步,成功解决了Chrome iOS的问题
- 在代码里加了异常检测逻辑:当
isPlaying为true但actualIsPlaying为false时,延迟2秒后尝试重新替换音频源并播放,但对Safari真机无效
想请教的问题
- 有没有同学碰到过这种iOS真机Safari上Expo Audio自动播放失效的情况?你们是怎么解决的?
- 有没有什么“预激活”音频上下文的技巧,能让用户只点一次播放就能正常走完全流程,不需要额外手动触发?
核心代码片段
import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio"; import React, { useCallback, useEffect, useRef } from "react"; import AudioControls from "./AudioControls"; interface Cell { text: string; audioUrl: string | null; isPause: boolean; } interface AudioPlayerProps { cells: Cell[]; currentCellIndex: number; onCellChange: (index: number) => void; isPlaying: boolean; onPlayStateChange: (playing: boolean) => void; onTranscriptPress?: () => void; onFinish?: () => void; onPauseTimerStateChange?: (isActive: boolean) => void; onPlaybackBlocked?: (reason: string) => void; } export default function AudioPlayer({ cells, currentCellIndex, onCellChange, isPlaying, onPlayStateChange, onTranscriptPress, onFinish, onPlaybackBlocked, }: AudioPlayerProps) { const currentAudioUrl = cells[currentCellIndex]?.audioUrl ?? require("../../assets/5-seconds-of-silence.mp3"); const player = useAudioPlayer(); const { didJustFinish, playing: actualIsPlaying } = useAudioPlayerStatus(player); const isPlayingRef = useRef(isPlaying); // 播放速率设为1.0 const PLAYBACK_RATE = 1.0; // 检测预期播放但实际未播放的异常 useEffect(() => { console.log("isActuallyPlaying", actualIsPlaying); if (isPlaying && !actualIsPlaying) { const detectionDelay = 2000; // 2秒延迟避免音频启动时误判 const timeoutId = setTimeout(() => { if (isPlaying && !actualIsPlaying) { const reason = "Browser autoplay restrictions or playback issues detected"; console.warn( "AudioPlayer: Detected discrepancy - expected playing but audio is not actually playing" ); console.warn( "This could be due to browser autoplay restrictions or other playback issues" ); onPlaybackBlocked?.(reason); // 尝试重新加载音频并播放 player.replace({ uri: currentAudioUrl }); player.play(); } }, detectionDelay); return () => clearTimeout(timeoutId); } }, [ isPlaying, actualIsPlaying, currentCellIndex, cells, player, onPlayStateChange, onPlaybackBlocked, currentAudioUrl, ]); const handleAudioComplete = useCallback(() => { console.log("handleAudioComplete", { currentCellIndex, cellsLength: cells.length, isPlaying: isPlayingRef.current, nextIndex: currentCellIndex + 1, }); if (!isPlayingRef.current) { console.log("handleAudioComplete: not playing, returning early"); return; } const nextIndex = currentCellIndex + 1; if (nextIndex < cells.length) { onCellChange(nextIndex); } else { onFinish?.(); } }, [currentCellIndex, cells.length, isPlayingRef, onCellChange, onFinish]); // 省略其他状态监听与控制逻辑 }
有没有什么办法能提前“激活”音频上下文,让第一次播放后的自动切换能正常工作?或者有没有我没注意到的iOS Safari播放限制细节?
内容来源于stack exchange
相关产品推荐
相关产品推荐

