
这是DFX系列的第三篇崩溃问题专题。让我们直接进入主题。Flutter 鸿蒙应用的崩溃问题主要来自三个不同的层面引擎底层、Dart代码层和ArkTS 层。三个层面的崩溃问题所涉及的关键词不同、严重程度不同、修法也是不同的。一、先分清是哪种崩溃问题三种崩溃类型崩溃类型发生层日志关键词常见原因Native 崩溃引擎底层(C)Caught signal SIG*空指针、GPU 驱动崩溃、内存被破坏Dart 异常Dart 代码Unhandled exception类型错误、数组越界、空指针ETS 异常ArkTS 代码Failed to handleMethodChannel 回调中出错TipDart 异常是最常见的通常是代码里的问题Native 崩溃问题就比较严重了可能是引擎问题 或 GPU 驱动问题ETS 异常通常会导致功能失效判断方法直接在日志里依次搜索关键字# 第 1 步:搜到了 → 引擎底层崩溃,看第二节 hdc shell hilog | grep Caught signal # 第 2 步:搜到了 → Dart 代码有 bug,看第三节 hdc shell hilog | grep Unhandled exception # 第 3 步:搜到了 → ArkTS 插件有 bug,看第四节;都没搜到,回去抓全量日志重新排查 hdc shell hilog | grep FLUTTER_ETS_EXCEPTION还不会抓日志的话先看上一篇的四步流程二、Native 崩溃引擎层2.1 这种崩溃问题是什么Native 崩溃是 Flutter 引擎的 C 代码发生了严重错误可以理解为「引擎自己崩了」。它比 Dart 异常严重得多引擎爆了车就瘫了引擎一旦崩溃整个应用直接闪退没有机会做任何清理。引擎在启动时会注册信号处理器作用类似烟雾报警器崩溃发生时报警器把崩溃信息记录下来输出到日志。2.2 崩溃信号类型下面的异常类型清单根据信号关键字查看对应初步原因信号名称通俗解释常见原因SIGSEGV段错误访问了不该访问的内存空指针、对象已释放但还在用SIGABRT异常终止引擎自己调用 abort() 退出断言失败、内存被破坏SIGFPE浮点异常数学运算出错除以零SIGBUS总线错误内存对齐有问题DMA buffer 被提前释放SIGTERM软件终止系统让进程退出系统资源限制SIGPIPE管道断裂往已关闭的管道写数据IPC 通信通道断了2.3 从现象判断信号类型看到的现象可能的信号说明无征兆突然闪退,没有任何 Dart 报错SIGSEGV最常见,引擎访问了非法内存闪退前日志有 CHECK failed 或 assertSIGABRT引擎内部的断言检查失败画面花屏、绿屏后闪退SIGSEGVGPU 内存被破坏退后台再回前台后闪退SIGSEGVGPU 上下文丢失后仍渲染闪退堆栈里有 GPU / RasterizerSIGSEGV / SIGABRTGPU 驱动崩溃闪退堆栈里有 Texture / external_textureSIGSEGV纹理被释放后还在访问2.4 日志长什么样在日志中搜索 Caught signal典型输出:E Flutter: Caught signal SIGSEGV during program execution. E Flutter: Frame 0: 0x7f8b2c3d4e50 FlutterRenderer::DrawFrame E Flutter: Frame 1: 0x7f8b2c3d4f60 Shell::OnDrawFrame E Flutter: Frame 2: 0x7f8b2c3d5070 fml::MessageLoopImpl::Run读法:第一行Caught signal SIGSEGV说明是段错误空指针类问题后面的 Frame 行是崩溃时的调用栈Frame 0 是崩溃发生的最直接位置Frame 1、Frame 2 是调用链的上层。函数名对应引擎源码能据此判断是哪块功能出了问题。2.5 排查问题可以分为四步第 1 步拿到完整堆栈。-A 30表示搜到关键词后再往后看 30 行hdc shell hilog | grep -A 30 Caught signal第 2 步按信号类型定排查方向信号排查方向具体要找什么SIGSEGV空指针 / 悬垂指针搜GpuReclaim(GPU 回收)、UnRegisterExternalTexture(纹理注销)SIGABRT断言失败 / 堆破坏搜CHECK failed、DCheck failedSIGFPE除零运算检查尺寸计算、帧率计算中是否有 0 值SIGBUS内存映射失效检查 DMA buffer 是否被提前释放第 3 步按堆栈函数名定位问题模块。函数名直接对应引擎源码的文件名显示 Unknown 的用 addr2line 把地址翻译成源码行号重点看 Frame 0addr2line -e libflutter.so -f -C 0x7f8b2c3d4e50第 4 步看崩溃之前的日志找原因就像查案要看案发前的监控录像一样。崩溃前 5-10 秒的日志通常包含触发原因-B 50表示往前看 50 行hdc shell hilog | grep -B 50 Caught signal2.6 四个场景场景 1GPU 驱动崩溃。堆栈里有 GPUSurface、Rasterizer、SkSurface 这些词时对号入座:原因怎么排查怎么修GPU 回收后没恢复就渲染搜 GpuReclaim,看是否有 Surface REBUILT确保 OnGrContextCreated 完成后再渲染(见 系列内存篇 第 3 节)Surface 重建失败搜 SetDisplayWindow failed检查 native_window 是否还有效Vulkan 驱动 bug检查是否用 Vulkan 后端尝试切换到 OpenGL ES 后端着色器太复杂检查是否有复杂的自定义 Shader简化 Fragment Shader场景 2纹理释放后还在访问。堆栈里有 Texture、external_texture、OHOSExternalTexture。类比快递箱已经扔了还有人去箱子里拿东西。常见三种注销纹理后原生侧还在写入(搜 UnRegisterExternalTexture修复是先停生产者再注销纹理,顺序不能反)外部 NativeImage 被提前释放(搜 SetExternalNativeImage确保外部资源生命周期比纹理长)GPU 上下文销毁后还引用 GPU 资源(搜 OnGrContextDestroyed确认 OnTextureUnregistered 释放了资源)。正确的注销顺序// 第 1 步:先停止原生侧的生产,比如暂停视频播放 stopProducer(); // 第 2 步:再注销纹理 textureRegistry.unregisterTexture(textureId);场景 3引擎重复释放。堆栈里有 ~Shell、Destroy信号是 SIGABRT。类比:退房时钥匙还了又还了一次。两种触发多次调用 engine.destroy()(搜 FLUTTER_ENGINE_DESTROY 出现次数加 null 检查确保只销毁一次)分栏模式误触发 onDestroy(检查 FlutterPage.onDestroy 调用时机)。正确的写法onAbilityDestroy() { this.flutterEngine?.destroy(); this.flutterEngine null; // 防止重复销毁 }场景 4Skia 渲染异常。堆栈里有 SkCanvas、SkSurface、GrDirectContext。三种对应CustomPainter 访问了已释放资源(检查是否持有外部引用确保不持有会被释放的外部资源)GPU 上下文丢失后继续渲染(搜 GpuReclaim在 OnGrContextDestroyed 后暂停渲染)Picture 跨 isolate 传递(用 toImage 转换后再传递)。三、Dart 异常代码层3.1 怎么理解最常见的崩溃原因Dart 代码里有未被 try-catch 捕获的异常比如调用了 null.toString()或者访问 list[100] 但列表只有 10 个元素。异常未捕获时isolate 退出应用闪退引擎自动捕获并记录到日志。这也是最容易修的崩溃类型日志里直接标注了出错的文件和行号。3.2 日志长什么样HiLog 输出:E Flutter: Unhandled exception: E Flutter: type Null is not a subtype of type String E Flutter: #0 main (package:myapp/main.dart:12:5) E Flutter: #1 _runMain (package:myapp/main.dart:8:3)如何理解第一行说明是未捕获的 Dart 异常第二行是错误原因试图把 null 当成 String 用第三行开始是堆栈格式为「#帧号 函数名 (文件:行号:列号)」,#0 是最直接的出错位置package:myapp/main.dart:12:5指 main.dart 第 12 行第 5 列。直接打开日志标注的文件和行号通常一眼就能看出问题。3.3 排查四步走第 1 步拿完整堆栈hdc shell hilog | grep -A 30 Unhandled exception第 2 步打开日志标注的代码文件,定位到行。从 #0 开始看找package:应用名/开头的行那是自己的代码。dart: 和 package:flutter/ 开头的是框架代码,通常不是问题源。第 3 步按异常类型判断原因:异常类型通俗解释怎么修NoSuchMethodError在 null 上调用了方法加空判断:obj?.toString()TypeError类型不匹配安全转换:value?.toString() ?? ‘’RangeError索引越界加边界检查StateError对象状态不对检查对象状态FormatException格式不对检查输入格式Stack Overflow栈溢出,递归没有终止条件加递归终止条件LateInitializationErrorlate 变量没初始化就用了确保使用前已赋值第 4 步用 DevTools 直观调试:flutter run --ohos 启动浏览器打开 DevTools开启「Stop on uncaught exceptions」,复现问题调试器会在出错的地方暂停所有变量的值都能看到。3.4 五个高频问题与修复问题 1空指针最常见。user 可能为 null 时直接链式调用// user 是 null 时,这里就崩了String name user.name.toUpperCase();用 ?. 安全调用加默认值:String name user?.name?.toUpperCase() ?? unknown;问题 2异步异常没捕获。Future 没有 await 时try-catch 拦不住里面的异常// 没有 await,这个异常不会被外层 try-catch 捕获 Future.delayed(Duration(seconds: 1), () { throw Exception(异步出错); });三种修法按优先级排局部用 await try-catch或 catchError全局兜底用 runZonedGuarded所有未捕获的异常都会进回调// 推荐:全局兜底,在 main 中用 runZonedGuarded runZonedGuarded(() { runApp(MyApp()); }, (error, stackTrace) { print(全局捕获: $error\n$stackTrace); });问题 3类型转换出错。JSON 字段为 null 或类型不符时as 强转会崩// name 是 null 崩;price 是 100(int)也崩 String name json[name] as String; double price json[price] as double; // 安全的字符串转换、int 和 double 都能处理的数字转换 String name json[name]?.toString() ?? ; double price (json[price] as num)?.toDouble() ?? 0.0;问题 4数组越界。两种修法边界检查或 elementAtOrNull(Dart 3.0越界返回 null 而不是崩溃)// 方法 1:边界检查 if (index 0 index items.length) { var item items[index]; } // 方法 2:elementAtOrNull(Dart 3.0) var item items.elementAtOrNull(index);问题 5MethodChannel 回调里出错。handler 里抛异常会闪退包一层 try-catch 返回默认值_methodChannel.setMethodCallHandler((call) async { try { if (call.method getData) { return await _loadData(); } } catch (e, s) { print(MethodChannel 出错: $e\n$s); return null; // 返回默认值,不要让异常跑出去 } });3.5 边界问题三个限制提前知道错误信息最长 4096 字符、堆栈最长 8192 字符超出会被截断发现信息不完整,去 HiLog 里搜完整版系统 API 低于 18 时不生成 HiAppEvent 事件但 HiLog 仍然有。四、ETS 异常:插件层4.1 是什么ETS 是 ArkTS 的简称鸿蒙的编程语言。ArkTS 插件代码在处理 MethodChannel 消息时出错就会产生 ETS 异常。三个特点通常不会导致闪退(被 try-catch 捕获了)但会导致某个功能用不了典型表现是 MethodChannel 调用无响应用第三方插件时这个问题可能是插件代码的 bug。4.2 日志长什么样搜 Failed to handle 或 FLUTTER_ETS_EXCEPTION,典型输出:E Flutter: MethodChannel# Failed to handle method call E Flutter: TypeError: Cannot read property length of undefined E Flutter: at MethodChannel.onMessage (MethodChannel.ets:227:15)怎么理解第一行说明 MethodChannel 消息处理失败第二行是原因读取了 undefined 的 length 属性第三行是出错位置MethodChannel.ets 第 227 行。4.3 排查分为三步第 1 步搜 ETS 异常日志hdc shell hilog | grep -E FLUTTER_ETS_EXCEPTION|Failed to handle|Uncaught exception in第 2 步按事件里的 CONTEXT 字段确定异常发生在哪个环节CONTEXT 值排查方向MethodChannel.onMessage插件接收消息时出错,检查插件的 onMethodCall 方法MethodChannel.reply处理返回结果时出错,检查 Dart 侧的回调代码DartMessenger.invokeHandler二进制消息处理出错,检查 BinaryMessageHandlerDartMessenger.handlePlatformMessageResponse消息回复处理出错,检查 BinaryReply 回调第 3 步打开出错的 ETS 文件定位行号。STACK_TRACE 格式是「at 函数名 (文件:行号:列号)」,重点关注 undefined / null 访问。4.4 两个高频问题与修复问题 1MethodChannel 参数为 undefined。argument() 返回 undefined 时直接取属性// name 是 undefined 时,undefined.length 崩 onMethodCall(call: MethodCall, result: MethodResult): void { const name: string call.argument(name); const length name.length; }问题 2消息编解码不匹配。Dart 侧和 ETS 侧用了不同的 Codec,参数解析失败。修法是确保两侧一致默认都用 StandardMethodCodec自定义 Codec 时两侧相同。类型对照Dart int 对 ETS numberString 对 stringbool 对 booleanList 对 ArrayMap 对 object。4.5 特点ETS 异常被框架捕获后不会闪退MethodChannel.onMessage 捕获后自动返回 error 给 Dart 侧DartMessenger.invokeHandler 捕获后发送空回复。代价是功能受影响消息处理失败了。五、崩溃排查指南遇到崩溃,按顺序走第一步确认崩溃类型抓全量日志hdc shell hilog flutter_log.txt;搜 Caught signal有就是 Native 崩溃搜 Unhandled exception有就是 Dart 异常搜 FLUTTER_ETS_EXCEPTION / Failed to handle有就是 ETS 异常。Native 崩溃场景记录信号类型记录堆栈中的函数名和地址Unknown 的用 addr2line 解析搜崩溃前 50 行日志找导火索搜 GpuReclaim 确认是否和 GPU 有关搜 UnRegisterExternalTexture 确认是否和纹理有关。Dart 异常场景读完整堆栈找 package应用名/ 的行打开对应文件和行号确认异常类型检查是不是异步异常(Future 没 await、没 catchError)用 DevTools 的「Stop on uncaught exceptions」调试。ETS 异常场景按 CONTEXT 确定环节打开 ETS 文件和行号检查参数是否为 undefined / null检查参数类型是否匹配通用记录崩溃前的操作场景(点了什么按钮、进了什么页面)确认是否能稳定复现能复现的 bug 最好修。六、HiAppEvent 事件参数两类崩溃事件的字段写脚本解析日志时用得上。FLUTTER_DART_EXCEPTION参数类型限制说明frameworkNameString—固定 “FLUTTER”errorMsgString≤4096 字符异常错误信息stackTraceString≤8192 字符异常堆栈pidInt64—进程 IDtimeStampInt64—事件时间,UTC 毫秒FLUTTER_ETS_EXCEPTION参数类型说明EVENT_NAMEString固定 “FLUTTER_ETS_EXCEPTION”CONTEXTString异常发生环节,见第四节 CONTEXT 表ERROR_MSGString异常错误信息STACK_TRACEString异常堆栈下一篇展开卡顿问题专项持续期待下~小伙伴们记得点赞关注CPF-Flutter社区官网https://atomgit.com/CPF-Flutter“AI再牛技术不能丢”