问题现象
在开发Flutter视频播放器时,遇到了一个非常有意思的现象:
- 当前播放第3个片段,向后跳到6等不正常,会先跳到片段6,然后再跳到4
这个现象非常奇怪?
问题代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void _switchToSegment(int index) { await _processSegmentBoundaries(newSegment); _controller!.play(); }
void _videoListener() { if (currentPosition >= adjustedEndTime) { _handleNextSegment(); } }
|
调试发现的问题
当调用 _switchToSegment(index)
时:
- 向后跳(如3→6):
- 调用
_switchToSegment(6)
- 视频跳转到第6个片段的开始位置
_videoListener
检测当前播放位置,发现已经大于原片段的 adjustedEndTime
- 那么则调用_handleNextSegment方法, 跳转到3的下一个片段4
核心问题:_videoListener
在 _switchToSegment
还没完全初始化完成时就开始工作了。
解决思路
问题的本质是竞态条件:用户手动切换和自动切换逻辑产生了冲突。
解决思路很简单:加一个标志位,让这两个操作互相隔离。
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class VideoPlayerState extends State<VideoPlayer> { bool _isSwitchingSegment = false; void _videoListener() { if (_isSwitchingSegment) { return; } if (currentPosition >= adjustedEndTime) { _handleNextSegment(); } } Future<void> _switchToSegment(int index) async { _isSwitchingSegment = true; try { await _processSegmentBoundaries(newSegment); setState(() { _currentSegmentIndex = index; _isPlaying = true; }); _controller!.play(); } finally { _isSwitchingSegment = false; } } }
|
总结
这个bug很好地展示了异步编程中的经典问题:竞态条件。
- 现象:看似简单的功能,却出现了方向性的差异
- 本质:两个异步操作之间的时序冲突
- 解决:通过状态标志位进行操作隔离
这提醒我们在处理用户交互和自动逻辑并存的场景时,一定要考虑它们之间的相互影响,必要时需要加入状态管理来避免冲突。