Flutter Android应用Firebase Crashlytics莫名崩溃排查求助
问题描述
我开发了一款面向Android平台的Flutter应用,提交至Play Store内部测试及Firebase Robo测试均未发现异常,但Firebase Crashlytics报告了大量来自谷歌设备的崩溃,主要错误为:
Fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError:
Null check operator used on a null value. Error thrown null.
查看堆栈跟踪后无法理解其中含义,尝试使用flutter symbolize命令反混淆跟踪信息后仍无法解读:
flutter symbolize -i ./build/symbols/stack.txt -d ./build/symbols/app.android-arm64.symbols |more
不确定这是否是Crashlytics的误报,也不知该如何展开排查,求助解决思路。
完整Crashlytics堆栈跟踪:
# Crashlytics - Stack trace # Application: app.myapp # Platform: android # Version: 1.0.11 (11) # Issue: 0e1a67110958092c5b5d627bd3bbf313 # Session: 6379F18601E600C92CAE49F9D7E36C5A_DNE_0_v2 # Date: Sun Nov 20 2022 22:21:10 GMT+1300 Fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: Null check operator used on a null value. Error thrown null. BuildId: 4269d6000abb454d5f35d98538efa35f #00 abs 0 virt 00000000005f5f67 _kDartIsolateSnapshotInstructions+0x4717c7 #01 abs 0 virt 00000000005c92de _kDartIsolateSnapshotInstructions+0x444b3e #02 abs 0 virt 00000000001957da _kDartIsolateSnapshotInstructions+0x1103a #03 abs 0 virt 000000000019576d _kDartIsolateSnapshotInstructions+0x10fcd #04 abs 0 virt 00000000001952ee _kDartIsolateSnapshotInstructions+0x10b4e pool-63-thread-1: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.park [LockSupport.java:194]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await [AbstractQueuedSynchronizer.java:2081]) #03 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:1176]) #04 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:905]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1063]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #07 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #08 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012]) pool-79-thread-1: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.park [LockSupport.java:194]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await [AbstractQueuedSynchronizer.java:2081]) #03 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:1176]) #04 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:905]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1063]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #07 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #08 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012]) . . . ScionFrontendApi: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.parkNanos [LockSupport.java:234]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos [AbstractQueuedSynchronizer.java:2123]) #03 pc 0x0 app.myapp (java.util.concurrent.LinkedBlockingQueue.poll [LinkedBlockingQueue.java:458]) #04 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1062]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #07 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012]) pool-146-thread-1: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.park [LockSupport.java:194]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await [AbstractQueuedSynchronizer.java:2081]) #03 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:1176]) #04 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:905]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1063]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #07 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #08 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012]) pool-88-thread-1: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.park [LockSupport.java:194]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await [AbstractQueuedSynchronizer.java:2081]) #03 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:1176]) #04 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:905]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1063]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #07 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #08 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012]) pool-102-thread-1: #00 pc 0x0 app.myapp #01 pc 0x0 app.myapp (java.util.concurrent.locks.LockSupport.park [LockSupport.java:194]) #02 pc 0x0 app.myapp (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await [AbstractQueuedSynchronizer.java:2081]) #03 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:1176]) #04 pc 0x0 app.myapp (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take [ScheduledThreadPoolExecutor.java:905]) #05 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.getTask [ThreadPoolExecutor.java:1063]) #06 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor.runWorker [ThreadPoolExecutor.java:1123]) #07 pc 0x0 app.myapp (java.util.concurrent.ThreadPoolExecutor$Worker.run [ThreadPoolExecutor.java:637]) #08 pc 0x0 app.myapp (java.lang.Thread.run [Thread.java:1012])
排查解决思路
- 先确认是否为Crashlytics误报:查看崩溃的设备明细,看是否集中在特定谷歌设备/Android版本;统计崩溃发生的场景(如后台运行、特定操作后),如果大量不同用户出现相同崩溃,基本可以排除误报。
- 修复符号化失败问题:
- 确保符号文件与崩溃版本的APK完全匹配(Flutter编译时生成的symbols文件需对应同一版本的release包);
- 直接在Firebase控制台的Crashlytics模块上传符号文件,避免本地命令行操作的误差;
- 编译release包时,使用
flutter build apk --split-per-abi命令生成对应架构的符号文件,保证符号与设备架构匹配。
- 主动复现Null check崩溃:
- 全局搜索代码中所有
!操作符,重点检查异步操作(如Future返回null、网络请求失败)、平台通道调用(Android原生返回null)、状态管理中的可空变量; - 使用谷歌Pixel系列模拟器/实体设备测试,模拟低内存、网络不稳定、后台切换等边缘场景;
- 开启Flutter严格空安全模式,在
main.dart中添加错误捕获逻辑,或直接用flutter run --release在本地测试release版本(debug版本的空检查逻辑与release存在差异)。
- 全局搜索代码中所有
- 补充日志定位上下文:在可能出现null的关键节点添加日志,发布带日志的测试版本到内部测试,让Crashlytics收集更多崩溃前的变量值和操作路径。
- 排查第三方依赖兼容性:检查与谷歌服务、原生交互相关的依赖库,尝试暂时禁用部分依赖,验证崩溃是否消失,定位问题依赖。
- 处理线程相关问题:堆栈中显示多个线程池处于等待状态,但需确认崩溃是否发生在Isolate等后台线程的Dart代码中,可在Isolate内添加错误捕获逻辑,避免未处理的空检查错误。
内容的提问来源于stack exchange,提问作者under
相关产品推荐
相关产品推荐

