Flutter应用拍照时随机崩溃问题排查求助
问题描述
我正在开发一款包含拍照并上传至后端的Flutter应用,整体功能正常。为确保向服务器传输数据的安全性,我实现了App从Paused/Exited状态切换至Resumed状态时验证连接token有效性的逻辑。
但在三星S20 FE(Android 12)设备上测试时,拍照会随机导致应用崩溃,Android Studio捕获到的错误信息如下:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Error During Communication: Error occured while Communication with Server with StatusCode: 404
奇怪的是,我们仅在按下按钮后才会上传图片,而崩溃却发生在拍照完成瞬间。我怀疑是生命周期状态检测代码的问题——该代码会调用后端验证token,且已用.then和.onError包裹,理论上应能捕获网络错误。
这个问题在OnePlus 6(Android 11)及模拟器上均无法复现。以下是相关功能代码,求问问题原因及修复方案:
App生命周期函数
@override Future<void> didChangeAppLifecycleState(AppLifecycleState state) async { print("State changed to: $state"); switch(state){ case AppLifecycleState.resumed: final LoginManager loginManager = locator<LoginManager>(); final userToken = await loginManager.getUserTokenFromStorage(); if (userToken != null) { final Future<LoginResponse?> loginResponse = locator<LoginManager>().verifyToken(refreshToken); loginResponse.then((value) { if(value == null) { _showTimeoutDialog(navigatorKey.currentContext!); } }).onError((error, stackTrace) { _showTimeoutDialog(navigatorKey.currentContext!); }); } break; case AppLifecycleState.inactive: // TODO: Handle this case. break; case AppLifecycleState.paused: // TODO: Handle this case. break; case AppLifecycleState.detached: // TODO: Handle this case. break; } }
注:showTimeoutDialog用于显示弹窗并正常断开用户连接,不会传输任何信息。
拍照函数
Future<File?> _getPhotoFromCamera(bool isSelfie) async { return ImagePicker() .pickImage( source: ImageSource.camera, preferredCameraDevice: isSelfie ? CameraDevice.front : CameraDevice.rear, ) .then((value) => value != null ? File(value.path) : null); }
使用flutter image_picker包。
问题原因及修复方案
核心原因
- Android 12生命周期行为差异:三星S20 FE运行Android 12,调用系统相机时,App可能被系统彻底暂停甚至销毁(而非仅进入inactive/paused状态)。拍照完成返回App触发
resumed状态时,navigatorKey.currentContext可能尚未完全初始化,直接用!强制非空会导致上下文为空,进而引发未捕获异常——你看到的404是网络请求结果,但真正崩溃的原因是后续调用弹窗时上下文为空,只是异常栈被网络错误覆盖。 - 未完全捕获的异步异常:
verifyToken内部可能存在未被捕获的异常,或者refreshToken未正确初始化(比如为null),导致请求URL错误返回404,且该异常未被链式调用完全捕获。
修复步骤
1. 确保上下文非空再调用弹窗
修改resumed状态逻辑,先判断上下文是否就绪,避免强制非空:
case AppLifecycleState.resumed: final LoginManager loginManager = locator<LoginManager>(); final userToken = await loginManager.getUserTokenFromStorage(); final context = navigatorKey.currentContext; if (userToken != null) { if (context == null) { // 上下文未就绪,延迟重试 Future.delayed(const Duration(milliseconds: 500), () { _verifyTokenAndShowDialog(); }); break; } locator<LoginManager>().verifyToken(refreshToken) .then((value) { if(value == null) { _showTimeoutDialog(context); } }) .onError((error, stackTrace) { _showTimeoutDialog(context); }); } break;
新增辅助方法:
void _verifyTokenAndShowDialog() async { final LoginManager loginManager = locator<LoginManager>(); final userToken = await loginManager.getUserTokenFromStorage(); final context = navigatorKey.currentContext; if (userToken != null && context != null) { locator<LoginManager>().verifyToken(refreshToken) .then((value) { if(value == null) { _showTimeoutDialog(context); } }) .onError((error, stackTrace) { _showTimeoutDialog(context); }); } }
2. 校验refreshToken有效性
确保调用verifyToken时refreshToken不为null,避免因参数错误导致404:
if (userToken != null && refreshToken != null) { // 执行验证逻辑 }
3. 用try-catch增强异常捕获
替换链式调用为try-catch结构,更直观捕获所有异常:
case AppLifecycleState.resumed: final LoginManager loginManager = locator<LoginManager>(); final userToken = await loginManager.getUserTokenFromStorage(); final context = navigatorKey.currentContext; if (userToken != null && context != null && refreshToken != null) { try { final LoginResponse? value = await locator<LoginManager>().verifyToken(refreshToken); if(value == null) { _showTimeoutDialog(context); } } catch (e, stackTrace) { print("Token验证异常: $e\n$stackTrace"); _showTimeoutDialog(context); } } break;
4. 适配Android 12相机生命周期
在AndroidManifest.xml中添加配置,降低App被系统销毁的概率:
<application ... android:allowBackup="true" android:persistent="false"> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </application>
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

