优化Expo AV实现FlatList中高效视频缓存方案咨询
解决方案
核心思路
通过分阶段加载视频内容实现需求:未播放时仅加载并缓存前2秒片段,用户触发播放后再加载完整视频并缓存剩余内容。结合Expo AV的加载参数配置与组件状态管理,配合FlatList的内存优化机制来实现。
1. 视频项组件的分阶段加载实现
每个视频项维护播放状态与加载状态,初始加载2秒预览片段,播放时切换为完整视频加载:
import { Video } from 'expo-av'; import { useState, useEffect, useRef } from 'react'; import { TouchableOpacity, Text, View } from 'react-native'; const VideoListItem = ({ videoUri }) => { const [isPlaying, setIsPlaying] = useState(false); const [isFullLoaded, setIsFullLoaded] = useState(false); const videoRef = useRef(null); // 加载2秒预览片段 const loadPreviewSegment = async () => { if (!videoRef.current) return; try { await videoRef.current.loadAsync( { uri: videoUri }, { startTime: 0, endTime: 2, shouldPlay: false, cachePolicy: 'cache-only', // 仅缓存加载的2秒片段 } ); } catch (err) { console.error('预览片段加载失败:', err); } }; // 加载完整视频 const loadFullVideo = async () => { if (!videoRef.current) return; try { await videoRef.current.loadAsync( { uri: videoUri }, { shouldPlay: true, cachePolicy: 'cache-then-network', // 优先用缓存,再请求剩余内容 } ); setIsFullLoaded(true); } catch (err) { console.error('完整视频加载失败:', err); } }; // 处理播放/暂停逻辑 const togglePlayback = async () => { if (!videoRef.current) return; if (!isFullLoaded) { loadFullVideo(); setIsPlaying(true); } else { if (isPlaying) { await videoRef.current.pauseAsync(); } else { await videoRef.current.playAsync(); } setIsPlaying(!isPlaying); } }; // 组件挂载时加载预览,卸载时释放资源 useEffect(() => { loadPreviewSegment(); return () => { if (videoRef.current) { videoRef.current.unloadAsync(); // 卸载视频,释放内存缓存 } }; }, [videoUri]); return ( <View style={{ marginBottom: 10 }}> <Video ref={videoRef} style={{ width: '100%', height: 250 }} resizeMode="cover" onPlaybackStatusUpdate={(status) => { if (status.didJustFinish) { setIsPlaying(false); } }} /> <TouchableOpacity onPress={togglePlayback} style={{ padding: 10, backgroundColor: '#eee' }} > <Text>{isPlaying ? '暂停' : '播放'}</Text> </TouchableOpacity> </View> ); }; export default VideoListItem;
2. FlatList的内存优化配置
针对无限滚动场景,优化FlatList的渲染与资源释放,避免内存堆积:
import { FlatList } from 'react-native'; import VideoListItem from './VideoListItem'; const VideoScrollList = ({ videoList }) => { return ( <FlatList data={videoList} renderItem={({ item }) => <VideoListItem videoUri={item.uri} />} keyExtractor={(item) => item.id} removeClippedSubviews={true} // 自动卸载不可见区域的组件 windowSize={5} // 仅渲染当前可见区域前后5个视频项 initialNumToRender={3} // 初始渲染3个视频项 /> ); }; export default VideoScrollList;
3. 关键注意事项
- 缓存策略验证:确保你的Expo SDK版本支持
startTime/endTime与cachePolicy参数的组合使用,建议使用最新稳定版Expo SDK。 - 资源释放:必须在组件卸载时调用
unloadAsync(),否则Expo AV会持续占用内存缓存已加载的视频内容。 - 网络兼容性:如果视频服务器不支持HTTP范围请求,
startTime/endTime参数可能无法生效,此时可以考虑预先生成2秒预览片段的独立URL。 - 状态复用:对于已播放过的视频,可以在全局状态中标记为"已完整加载",下次渲染时直接加载完整视频,提升用户体验。
内容的提问来源于stack exchange,提问作者omar
相关产品推荐
相关产品推荐

