[英]Handling Media Playback State in React
目前正在使用 expo + React Native 並遇到有效處理媒體播放 state 的挑戰。 通過 expo 的AV 庫,我們得到了一個圍繞播放資源的命令式接口,帶有一個掛鈎播放 state 更新的參數:
// this runs every 50ms (or faster) during active playback
const handlePlaybackStatusUpdate = (nextStatus: AVPlaybackStatus) => {
// do something in here
}
const loadedSound = (await Audio.Sound.createAsync({ uri }, playbackConfig, handlePlaybackStatusUpdate)).sound
// ...
await loadedSound.playAsync()
了解“高性能”受到RN 應用程序中線程通信限制的限制,
我的實施目標是:
我一直在努力尋找一種有效處理這些播放狀態更新的方法。 進一步的上下文。 播放狀態暴露了播放的許多維度 state - isPlaying, isBuffering, didJustFinish, durationMillis (播放長度以毫秒為單位), positionMillis (播放中的當前 position) 等。根據實驗和觀察,positionMillis 是絕大多數情況下唯一變化的值更新(除了那些與 playAsync、pauseAsync 等命令相關的更新)因此,以下嘗試公開基於測量 position 的 stream 更新,獨立於上下文 state 的 rest。
我嘗試過的一些事情:
const PlaybackProvider = ({ children }: { children: ReactNode }) => {
const [playbackStatus, setPlaybackStatus] = useState<PlaybackStatus>()
// ...
await Audio.Sound.createAsync({ uri }, playbackConfig, setPlaybackStatus)
const usePlaybackPosition = (
onUpdate: (position: number, duration: number) => Promise<void> | void,
interval = 200
) => {
const { playbackStatusRef } = usePlayback()
const [timer, setTimer] = useState(0)
useInterval(() => {
const { positionMillis, durationMillis } = playbackStatusRef?.current ?? {}
onUpdate(positionMillis, durationMillis)
setTimer(timer + 1)
}, interval)
}
const handlePlaybackStatusUpdate = (newStatus?: AVPlaybackStatus): void => {
// save a ref for components that want to listen to position
playbackStatusRef.current = newStatus
if (newStatus === undefined) {
setPlaybackStatus(PlaybackStatus.None)
return
}
if (!newStatus.isLoaded) {
if (newStatus.error) {
console.log(`Encountered a fatal error during playback: ${newStatus.error}`)
setPlaybackStatus(PlaybackStatus.Error)
return
}
} else {
const { isPlaying, isBuffering, positionMillis, didJustFinish, isLooping } = newStatus
let nextStatus: PlaybackStatus = PlaybackStatus.Idle
if (isPlaying) {
nextStatus = PlaybackStatus.Playing
} else if (positionMillis > 0) {
nextStatus = PlaybackStatus.Paused
} else {
// not playing and position is 0
nextStatus = PlaybackStatus.Idle
}
if (didJustFinish && !isLooping) {
nextStatus = PlaybackStatus.Finished
}
if (nextStatus !== playbackStatus) {
// this conditional ends up running TONS of times before playbackStatus actually changes
setPlaybackStatus(nextStatus)
}
}
}
我希望保持簡單和正統的解決方案 re: React 最佳實踐。 盡管 React 文檔表明媒體播放是對 ref 的有效使用,所以可能只需要提交並強化基於 ref 的接口。 我希望組件層次結構的更智能組合/重構+復制/共享行為的掛鈎/上下文就足夠了。
上面可能有一些不正確的假設。 如果能指出這些,我將不勝感激。 同樣,非常感謝關於 expo/react-native 中媒體播放的已知限制/問題的任何和所有見解。 感謝您的幫助!
我們所做的一項優化是onPlaybackStatusUpdate
鈎子的抖動。 僅每n
毫秒處理一次更新。 分享一段代碼供參考(不是完整的實現):
const debouncedPlaybackStatusUpdate = useMemo(
() => debounce(onPlaybackStatusUpdate, 100),
[],
);
async function playSound(status: boolean) {
if (!status) {
await soundObj.playAsync();
soundObj.setOnPlaybackStatusUpdate(debouncedPlaybackStatusUpdate);
} else {
await soundObj.pauseAsync();
}
}
這應該減少大部分重新渲染。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.