使用audio_service重启App后初始化失败:'_cacheManager == null'断言错误
问题描述
在Flutter应用中使用audio_service实现音频播放功能,用户登出时需要清除缓存并重启应用,但重启后重新初始化audio_service时触发断言错误:
flutter: 'package:audio_service/audio_service.dart': Failed assertion: line 993 pos 12: '_cacheManager == null': is not true.
该错误导致AudioService无法正常初始化。
相关代码片段
触发重启的按钮代码
FilledButton( onPressed: () { BlocProvider.of<PlayerCubit>(context).clearCache(); RestartWidget.restartApp(context); }, child: const Text('Restart App'), ),
应用初始化代码
Future<void> runAudioApp() async { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); await _initServices(); runApp( const RestartWidget( onRestart: _initServices, child: AudioApp(), ), ); } Future<void> _initServices() async { print('RestartWidget _initServices()'); await di.init(); }
RestartWidget实现
class RestartWidget extends StatefulWidget { const RestartWidget({ required this.onRestart, required this.child, super.key, }); final Widget child; final void Function() onRestart; static void restartApp(BuildContext context) { context.findAncestorStateOfType<_RestartWidgetState>()?.restartApp(); } @override State<RestartWidget> createState() => _RestartWidgetState(); } class _RestartWidgetState extends State<RestartWidget> { Key key = UniqueKey(); void restartApp() { setState(() { widget.onRestart.call(); key = UniqueKey(); }); } @override Widget build(BuildContext context) => KeyedSubtree( key: key, child: widget.child, ); }
依赖注入容器代码
final sl = GetIt.instance; Future<void> init() async { await sl.reset(); try { sl.registerSingleton(AppAudioCache()); if (!sl.isRegistered<AppAudioHandler>()) { try { sl.registerSingleton<AppAudioHandler>( await initAudioService(audioCache: sl()), ); } catch (e) { print(e); } } } catch (e) { print(e); } await sl.allReady(); }
用户疑问:
- 是否无法在App重启后重新初始化AudioService?
- 有没有办法彻底销毁旧实例以创建新实例?
- 当前重启方式是否有误?
解决方案
1. 问题根源
断言错误_cacheManager == null说明audio_service内部的缓存管理器实例未被正确销毁:虽然调用了GetIt的reset(),但audio_service绑定的原生层资源和全局静态状态依然残留;同时当前RestartWidget仅重建Widget树,未等待异步初始化完成就执行重建,导致资源释放不彻底。
2. 主动销毁AudioHandler实例
audio_service的AudioHandler初始化时会绑定原生服务,需主动调用dispose()释放资源,不能仅依赖GetIt重置。修改依赖注入的init()方法:
Future<void> init() async { // 先销毁已存在的AudioHandler实例 if (sl.isRegistered<AppAudioHandler>()) { await sl<AppAudioHandler>().dispose(); sl.unregister<AppAudioHandler>(); } // 重置GetIt容器 await sl.reset(); try { sl.registerSingleton(AppAudioCache()); // 重新注册AudioHandler sl.registerSingleton<AppAudioHandler>( await initAudioService(audioCache: sl()), ); } catch (e) { print(e); } await sl.allReady(); }
3. 优化重启逻辑
修改_RestartWidgetState的restartApp方法,确保先完成服务销毁和初始化,再重建Widget:
void restartApp() async { // 先执行销毁和初始化操作 await widget.onRestart(); // 再更新Key重建Widget树 setState(() { key = UniqueKey(); }); }
4. 彻底清理缓存
在重启前,除了业务缓存,还需清理audio_service相关缓存:
FilledButton( onPressed: () async { final playerCubit = BlocProvider.of<PlayerCubit>(context); // 清理业务缓存 playerCubit.clearCache(); // 清理audio_service缓存 if (sl.isRegistered<AppAudioCache>()) { await sl<AppAudioCache>().clearAll(); } // 重启应用 RestartWidget.restartApp(context); }, child: const Text('Restart App'), ),
5. 备选:原生级重启
如果上述方法仍无法解决全局状态残留问题,可调用原生平台API彻底重启应用进程:
- Android:使用
SystemNavigator.pop()配合启动页重定向,或第三方插件如flutter_restart - iOS:使用
exit(0)重启(注意App Store审核限制,仅在必要场景使用)
关键说明
audio_service设计为全局单例服务,不建议频繁销毁重建。登出场景更优方案是:停止播放任务、清理队列和缓存、直接切换到登录页,而非强制重启应用。仅在必须彻底重置所有状态的场景下,再采用上述重启+销毁实例的方案。
内容的提问来源于stack exchange,提问作者Vladyslav Ulianytskyi

