Flutter Android端Camera插件多次跳转后报Session已关闭错误如何解决?
崩溃根因
报错java.lang.IllegalStateException: Session has been closed; further changes are illegal.是因为相机捕获会话已经被系统/插件释放后,仍有异步回调尝试操作会话对象,触发非法状态异常。你每次进出页面都存在少量相机资源未完全释放的问题,累计到10次左右达到系统相机实例上限,就会必然崩溃,核心问题出在异步操作时序和资源释放逻辑的漏洞,以及你使用的0.9.4版本camera插件本身存在已知的资源释放bug。
修复方案
- 首先修复跳页前的资源预释放逻辑
你当前检测到驾照条码后直接执行跳页,此时相机图像流仍在运行,帧处理异步任务还在排队,跳页触发的dispose释放资源和后台回调操作出现时序冲突,需要在跳页前先暂停所有相机操作:if (bcode.type == BarcodeType.driverLicense) { BarcodeDriverLicense bcodeDL = bcode.value as BarcodeDriverLicense; String scannedName = bcodeDL.lastName! + ", " + bcodeDL.firstName!; // 新增:永久锁定帧处理,停止所有相机操作 isBusy = true; await _controller?.stopImageStream(); await _controller?.dispose(); _controller = null; // 新增:判断页面是否还存在再跳页 if (mounted) { Navigator.of(context).popAndPushNamed( AnotherScreen.ANOTHER_SCREEN_ROUTE, arguments: ScannedInfoArguments(scannedName)); } break; } - 补充页面销毁标记,拦截无效异步回调
dispose方法是同步执行的,你当前没有等待相机资源释放完成就销毁页面,很多异步回调会在页面销毁后才执行,需要加标记拦截无效操作:- 新增状态属性
bool _isDisposed = false; - 重写dispose方法:
@override void dispose() { _isDisposed = true; _stopLiveFeed(); barcodeScanner.close(); super.dispose(); }- 所有涉及_controller操作的位置都要加状态校验,比如帧处理入口:
还有控制器初始化回调也要加校验:Future _processCreateInputImage(CameraImage image) async { // 新增:页面已销毁/控制器未初始化直接返回 if (isBusy || _isDisposed || _controller == null || !_controller!.value.isInitialized) return; isBusy = true; // 原有逻辑不变 }_controller?.initialize().then((_) { if (!mounted || _isDisposed || _controller == null) { return; } _controller?.startImageStream(_processCreateInputImage); if (mounted) setState(() {}); }); - 新增状态属性
- 修复_stopLiveFeed的空安全逻辑
给_stopLiveFeed方法加空判断和异常捕获,避免操作已经释放的控制器:Future _stopLiveFeed() async { if (_controller != null) { try { await _controller?.stopImageStream(); await _controller?.dispose(); } catch (e) { // 忽略释放阶段的非法状态异常 } _controller = null; } } - 升级camera插件版本
你使用的0.9.4版本camera插件存在大量Android端Camera2会话管理的已知bug,可升级到camera: ^0.10.0+1,该版本官方修复了会话关闭后调用方法的崩溃问题。如果Flutter 2.5.1版本无法兼容高版本插件,可以在Android层的相机回调代码中手动捕获IllegalStateException异常规避崩溃。 - 优化相机初始化逻辑
每次执行_startLiveFeed前先调用一次_stopLiveFeed,确保上一次的相机资源已经完全释放,再初始化新的控制器实例。
内容的提问来源于stack exchange,提问作者handitan
相关产品推荐
相关产品推荐

