Flutter Android端video_player后台停止播放及崩溃日志问题
Android平台Flutter video_player后台播放失效问题
问题现象
在Android平台使用Flutter的video_player包播放视频,近期Flutter或该包更新后,切换应用到后台时视频停止播放,日志中出现Surface销毁相关错误信息。
代码示例
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver { int _counter = 0; late VideoPlayerController _controller; void _incrementCounter() { setState(() { _counter++; }); } @override void initState() { super.initState(); _controller = VideoPlayerController.networkUrl(Uri.parse( 'https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_20mb.mp4') ,videoPlayerOptions: VideoPlayerOptions( allowBackgroundPlayback: true, mixWithOthers: true),) ..initialize().then((_) { setState(() {}); }); WidgetsBinding.instance.addObserver(this); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: _controller.value.isInitialized ? AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer (_controller), ) : Container(), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _controller.value.isPlaying ? _controller.pause() : _controller.play(); }); }, child: Icon( _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused) { print("AppLifecycleState.paused"); } } }
设备日志
2025-03-13 10:04:21.783 8186-8186 flutter com.example.untitled4 I AppLifecycleState.paused 2025-03-13 10:04:21.784 8186-8186 SurfaceView@6b4b083 com.example.untitled4 D updateSurface: surface is not valid 2025-03-13 10:04:21.863 14716-15805 WindowManager system_server E win=Window{fd23590 u0 com.example.untitled4/com.example.untitled4.MainActivity} destroySurfaces: appStopped=true cleanupOnResume=false win.mWindowRemovalAllowed=false win.mRemoveOnExit=false win.mViewVisibility=8 caller=com.android.server.wm.ActivityRecord.destroySurfaces:7033 com.android.server.wm.ActivityRecord.destroySurfaces:7014 com.android.server.wm.ActivityRecord.activityStopped:7747 com.android.server.wm.ActivityClientController.activityStopped:321 android.app.IActivityClientController$Stub.onTransact:702 com.android.server.wm.ActivityClientController.onTransact:186 android.os.Binder.execTransactInternal:1380 2025-03-13 10:04:21.863 14716-15805 WindowManager system_server I Destroying surface Surface(name=com.example.untitled4/com.example.untitled4.MainActivity$_8186)/@0x709f3e3 called by com.android.server.wm.WindowStateAnimator.destroySurface:811 com.android.server.wm.WindowStateAnimator.destroySurfaceLocked:478 com.android.server.wm.WindowState.destroySurfaceUnchecked:4219 com.android.server.wm.WindowState.destroySurface:4193 com.android.server.wm.ActivityRecord.destroySurfaces:7033 com.android.server.wm.ActivityRecord.destroySurfaces:7014 com.android.server.wm.ActivityRecord.activityStopped:7747 com.android.server.wm.ActivityClientController.activityStopped:321
解决方案
1. 配置AndroidManifest.xml
Android 12及以上版本要求后台媒体播放必须使用前台服务,需在android/app/src/main/AndroidManifest.xml中添加权限和服务声明:
<!-- 添加权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application ...> <!-- 注册媒体播放服务 --> <service android:name="com.google.android.exoplayer2.ui.PlayerNotificationManager$MediaPlaybackService" android:foregroundServiceType="mediaPlayback" android:exported="false"/> </application>
2. 优化代码逻辑
- 完善生命周期回调,确保后台时音频继续播放:
@override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.paused) { // 后台时若视频正在播放,重新触发播放以保持音频运行 if (_controller.value.isPlaying) { _controller.play(); } } else if (state == AppLifecycleState.resumed) { // 回到前台刷新UI恢复视频画面 setState(() {}); } }
- 添加资源释放逻辑,避免内存泄漏:
@override void dispose() { WidgetsBinding.instance.removeObserver(this); _controller.dispose(); super.dispose(); }
3. 版本适配说明
video_player底层依赖ExoPlayer,近期版本对Android后台播放的权限校验更严格,必须通过前台服务声明才能让系统允许后台媒体播放。日志中的Surface销毁是应用后台时的正常系统行为,配置前台服务后,音频会独立于Surface继续播放。
内容的提问来源于stack exchange,提问作者AVEbrahimi
相关产品推荐
相关产品推荐

