Flutter应用onResume返回相机页面触发PlatformException求助
Flutter相机扫码后台唤起崩溃问题
我使用mlkit-barcode库实现二维码扫描功能,借助flutter camera包完成图像捕获和实时预览,期望应用退至后台再返回后所有功能仍正常运行。当前通过didChangeLifecycleState回调处理生命周期,常规场景无异常,但执行以下流程后会崩溃:
- 拍摄照片并跳转到第二个页面
- 返回相机页面
- 将应用退至后台再唤起
- 实时预览、扫码及拍照功能全部失效
捕获的异常日志
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.view.Surface android.media.ImageReader.getSurface()' on a null object reference, null, java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Surface android.media.ImageReader.getSurface()' on a null object reference
代码问题分析
- 页面返回逻辑错误:
DisplayPictureScreen中使用pushReplacement重新创建TakePictureScreen,导致原页面实例未被正确销毁,其WidgetsBindingObserver仍会响应生命周期回调,新旧实例的相机控制器产生资源冲突。 - 生命周期处理不严谨:
didChangeAppLifecycleState中未对控制器状态做细致判断,进入后台时直接销毁控制器,但唤起时的初始化流程未覆盖页面返回后的场景。 - 拍照后图像流未恢复:拍照时停止了图像流,但返回相机页面后未重新启动,后台唤起时也未处理该状态。
可行解决方案
1. 修复页面返回逻辑
移除DisplayPictureScreen中_onPopAsync的pushReplacement,改为直接返回上一页,避免重复创建页面实例:
Future<bool> _onPopAsync() async { Navigator.of(context).pop(); return true; }
2. 完善生命周期回调处理
增加控制器状态判断,避免无效操作,针对不同状态做精准处理:
@override void didChangeAppLifecycleState(AppLifecycleState state) { log(state.name); final CameraController? cameraController = _controller; if (cameraController == null) return; if (state == AppLifecycleState.inactive) { log('inactive being called'); cleanup(); } else if (state == AppLifecycleState.resumed) { log('on resume called'); if (_controller == null) { setUp(); } else if (!_controller!.value.isInitialized) { _controller!.initialize().then((_) { if (mounted) { _controller?.startImageStream(_processCameraImage); setState(() {}); } }); } else if (!_controller!.value.isStreamingImages) { _controller?.startImageStream(_processCameraImage); } } }
3. 拍照后恢复图像流
在拍照并返回相机页面后,重新启动图像流:
floatingActionButton: FloatingActionButton( onPressed: () async { try { if (!_controller!.value.isInitialized) { return; } await _controller!.stopImageStream(); final image = await _controller!.takePicture(); if (!mounted) return; await Navigator.of(context).push( MaterialPageRoute( builder: (context) => DisplayPictureScreen(imagePath: image.path), ), ); // 返回后恢复图像流 if (mounted && _controller != null && _controller!.value.isInitialized) { _controller?.startImageStream(_processCameraImage); } } catch (e) { print(e); } }, child: const Icon(Icons.camera_alt), ),
4. 优化资源清理方法
确保资源释放彻底,避免空指针异常:
Future cleanup() async { _canProcess = false; _isBusy = false; await _barcodeScanner.close(); if (_controller != null) { try { if (_controller!.value.isStreamingImages) { await _controller!.stopImageStream(); } if (_controller!.value.isInitialized) { await _controller!.dispose(); } } catch (e) { print(e); } _controller = null; } }
内容的提问来源于stack exchange,提问作者Stelios Papamichail
相关产品推荐
相关产品推荐

