Flutter开发APK在联想Tab M10 Plus上视频播放异常求助
问题解决方案
核心问题分析
从日志中的NO_MEMORY和解码器初始化失败报错来看,问题根源是视频控制器资源未正确释放,导致联想Tab M10 Plus的硬件解码器资源耗尽,无法初始化新的视频解码器。你的代码存在两个关键逻辑漏洞:
_currentMedia存储的是AspectRatioUI组件而非VideoPlayerController实例,导致_showNextMedia中的控制器释放逻辑完全失效_initializeVideoPlayerFuture从未被赋值,FutureBuilder的加载逻辑存在异常
修复后的完整代码
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; class SlideShowApp extends StatefulWidget { const SlideShowApp({ Key? key, required this.mediaUrls, required this.onSlideshowEnd, }) : super(key: key); final List<String> mediaUrls; final VoidCallback onSlideshowEnd; @override _SlideShowAppState createState() => _SlideShowAppState(); } class _SlideShowAppState extends State<SlideShowApp> { VideoPlayerController? _currentVideoController; Widget? _currentMediaWidget; int currentIndex = 0; Timer? _mediaTimer; @override void initState() { super.initState(); _loadCurrentMedia(); } void _loadCurrentMedia() { final url = widget.mediaUrls[currentIndex]; final fileType = getFileTypeFromUrl(url); // 先释放上一轮的资源 _currentVideoController?.dispose(); _currentVideoController = null; _mediaTimer?.cancel(); if (fileType == 'video') { _currentVideoController = VideoPlayerController.networkUrl(Uri.parse(url)) ..initialize().then((_) { setState(() { _currentMediaWidget = AspectRatio( aspectRatio: _currentVideoController!.value.aspectRatio, child: VideoPlayer(_currentVideoController!), ); }); _currentVideoController!.play(); // 监听视频自然结束事件,替代定时器(更准确) _currentVideoController!.addListener(_checkVideoEnd); }).catchError((error) { // 捕获视频初始化失败,直接跳转到下一个媒体 _showNextMedia(); }); } else if (fileType == 'image') { setState(() { _currentMediaWidget = Image.network( url, width: double.infinity, height: double.infinity, fit: BoxFit.cover, ); }); _mediaTimer = const Timer(Duration(seconds: 5), _showNextMedia); } else { setState(() { _currentMediaWidget = const SizedBox.shrink(); }); _mediaTimer = Timer(const Duration(seconds: 1), _showNextMedia); } } void _checkVideoEnd() { if (_currentVideoController != null && _currentVideoController!.value.isCompleted) { _currentVideoController!.removeListener(_checkVideoEnd); _showNextMedia(); } } void _showNextMedia() { _currentVideoController?.dispose(); _mediaTimer?.cancel(); currentIndex++; if (currentIndex < widget.mediaUrls.length) { _loadCurrentMedia(); } else { widget.onSlideshowEnd(); Navigator.pop(context); } } @override void dispose() { _currentVideoController?.dispose(); _mediaTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { if (_currentMediaWidget == null) { return const Center(child: CircularProgressIndicator()); } return _currentMediaWidget!; } } String getFileTypeFromUrl(String url) { final extension = url.split('.').last.toLowerCase(); if (extension == 'mp4') { return 'video'; } else if (extension == 'jpg' || extension == 'jpeg') { return 'image'; } return 'unknown'; }
关键修复点
- 分离控制器与UI组件:单独维护
_currentVideoController,确保每次切换媒体时都能正确释放解码器资源,避免硬件资源耗尽 - 替换视频定时器为播放结束监听:避免手动计算时长的误差,同时准确触发下一个媒体的加载
- 完善资源释放链路:在
initState、_showNextMedia、dispose中都添加了资源释放逻辑,彻底防止内存泄漏 - 添加错误捕获:在视频初始化失败时直接跳转下一个媒体,避免流程中断
额外优化建议
- 降低视频分辨率:如果设备硬件性能有限,可将视频转码为720P及以下分辨率,减少解码器负载
- 预加载下一个媒体:对于性能较弱的设备,可提前预加载下一个视频/图片资源,提升切换流畅度
- 添加加载失败占位:在图片/视频加载失败时显示占位UI,提升用户体验
内容的提问来源于stack exchange,提问作者PLWO
相关产品推荐
相关产品推荐

