# 应用闪退问题总结

#### 问题现象

应用在运行过程中发生的非预期终止或无响应行为，是影响用户体验的核心痛点。主要表现为以下三种形式：

*   **闪退 (Crash)**：应用界面突然消失，直接返回桌面。通常由代码层面的未捕获异常引起。
*   **卡死 (Freeze)**：应用界面画面静止，点击无反应，随后可能弹出“应用无响应”对话框或直接退出。通常由主线程阻塞引起。
*   **非预期退出 (Process Kill)**：应用在后台运行或前台切换时被系统强制关闭。通常涉及系统资源管控（内存、CPU、功耗）的介入。

#### 背景知识

定位稳定性问题，需要结合系统底层记录的“死亡现场”关键信息。主要依赖两类日志：

1.  **HiLog（实时日志）**：用于锁定应用被终止的**时间点**和**直接原因（Reason）**。
2.  **FaultLog（故障日志）**：系统自动生成的详细堆栈文件，存储在 /data/log/faultlog/faultlogger/，用于追溯**具体代码行号**。

系统终止进程时，会抛出不同的 reason，对应的故障类型及场景如下表所示：

1\. 代码崩溃类

_对应日志前缀：jscrash- / cppcrash-_。

| 退出 Reason | 故障归因 | 场景深度解析 |
| --- | --- | --- |
| JsCrash | JS/ArkTS 崩溃 | 业务代码抛出未捕获异常（如空指针、类型错误、语法错误、OOMError）。 |
| CppCrash | Native 崩溃 | C/C++ 层发生非法内存访问（SIGSEGV）、断言失败（SIGABRT）或系统库异常。 |

2\. 应用无响应类

_对应日志前缀：appfreeze-_。

| 退出 Reason | 故障归因 | 场景深度解析 |
| --- | --- | --- |
| THREAD_BLOCK_6S | 主线程卡死 | 主线程被锁阻塞、死循环或业务逻辑耗时过长，导致看门狗超时。 |
| APP_INPUT_BLOCK | 输入阻塞 | 用户点击/滑动屏幕后，主线程超过 6s 未分发处理事件。 |
| LIFECYCLE_TIMEOUT | 生命周期超时 | onCreate/onForeground 等生命周期回调执行耗时过长（通常 >3-5s）。 |

3\. 资源泄漏类

_对应日志：HiLog 或 resource\_leak_。

| 退出 Reason | 故障归因 | 场景深度解析 |
| --- | --- | --- |
| ResourceLeak:Fd Leak The number of fd exceeds... | 句柄泄漏 | 打开的文件、Socket、Handler 未关闭，耗尽系统 FD 资源（通常 >1024）。 |
| ResourceLeak:Thread Leak | 线程泄漏 | 线程只创建不销毁，数量爆炸导致 OOM 或 pthread_create 失败。 |
| ResourceLeak:Ion Leak | 图形内存泄漏 | Native 层 ION 内存（DMA/图形缓冲区）申请后未释放。 |
| ResourceLeak:Ashmem Leak | 共享内存泄漏 | 匿名共享内存（Ashmem）耗尽，常见于跨进程大数据传输未释放。 |
| RENDER_MEMORY_OVER_ERROR | 渲染内存超限 | RenderService 检测到应用占用渲染内存过大，强制管控。 |
| ResourceLeak:Pss Soft Kill | 内存软限超标 | 后台/低优先级清理：应用处于后台或非用户感知状态时，PSS 内存超过系统设定的“软阈值”（水位线），系统为预防 OOM 主动回收该进程。 |
| ResourceLeak:Pss Kill | 内存硬限超标 | 单进程内存过大：应用 PSS 内存持续增长，超过系统允许的单进程最大硬阈值（即使系统总内存充足），通常指示应用存在严重的堆内存泄漏。 |

4\. 内存与系统管控类

_对应日志：HiLog_。

| 退出 Reason | 故障归因 | 场景深度解析 |
| --- | --- | --- |
| LowMemoryKill | 系统低内存 | 整机内存不足，系统按优先级回收后台应用（标准 LMK）。 |
| SWAP_FULL | 虚拟内存耗尽 | 系统交换分区（Swap）已满，系统被迫杀进程释放空间。 |
| StabilityCheckKill | 稳定性检测 | 系统检测到进程频发崩溃或状态异常，进行保护性管控。 |
| CPU Highload | CPU 高负载 | 应用在后台长时间高占用 CPU（如死循环），发热严重被管控。 |
| Power Save Clean | 省电清理 | 超级省电模式或灭屏后，系统清理后台高耗电应用。 |

5\. 后台与冻结管控类

\*对应日志：HiLog \*。

| 退出 Reason | 故障归因 | 场景深度解析 |
| --- | --- | --- |
| ContinuouslyWakeupAbnormal | 异常唤醒 | 应用在后台频繁设置 Alarm 或持有锁，导致系统无法休眠，冻结失败后强杀。 |
| ILLEGAL_AUDIO_RENDERER... | 异常音频 | 应用后台挂起后仍试图播放音频，且未申请长时任务。 |
| ILLEGAL_AUDIO_CAPTURER... | 异常录音 | 应用后台挂起后仍试图占用麦克风，触发隐私/功耗管控。 |

6\. 正常退出与用户行为

_非故障类，属于预期内行为_。

| 退出 Reason | 归因分类 | 场景深度解析 |
| --- | --- | --- |
| KillApplicationSelf | 应用自杀 | 代码主动调用 terminateSelf 或 System.exit。 |
| User Request / Clear Session | 用户杀进程 | 用户在最近任务列表中划掉应用，或强制停止。 |
| UpgradeApp / UninstallApp | 安装卸载 | 应用更新或卸载导致的进程终止。 |

#### 问题定位

定位的核心逻辑遵循标准化路径：**“锁定时间点 → 识别退出原因 → 提取详细堆栈 → 针对性修复”**。

**第一步：锁定崩溃现场 (HiLog 分析)**

当问题复现时，通过系统实时日志找到“死亡 Reason”是定界的第一步。

*   **前置准备**：连接手机至电脑，确认应用包名（Bundle Name）。
*   **搜索指令**：在 Log 工具中开启**正则表达式 (Regex)** 搜索：
    
    ```
    PROCESS_KILL.*你的包名
    ```
    
    _(注：.\* 用于匹配进程ID等中间字符，精准定位针对目标包名的杀进程动作)_
    
*   **关键信息提取**：找到形如 I ProcessManager: PROCESS\_KILL: ... reason: JsCrash 的日志，**记录下 reason 字段的值**。

**第二步：获取故障堆栈 (FaultLog 提取)**

根据上一步获取的 reason，导出系统生成的堆栈文件进行深度分析。

*   **存储路径**：/data/log/faultlog/faultlogger/
*   **导出工具**：使用终端执行 hdc file recv 命令将文件导出到本地。

#### 分析结论

基于 PROCESS\_KILL 日志中的 reason 字段，可将问题快速定界为以下几类：

1.  **代码错误类 (JsCrash / CppCrash)**：
    *   **结论**：应用自身的 JS/ArkTS 或 C++ 代码存在逻辑缺陷或非法访问。
    *   **方向**：需排查具体的代码行号和变量状态。
2.  **主线程阻塞类 (THREAD\_BLOCK / APP\_INPUT / LIFECYCLE)**：
    *   **结论**：主线程负载过重或被阻塞，导致无法及时响应用户输入或系统回调。
    *   **方向**：需排查主线程中的耗时操作、死锁或死循环。
3.  **系统管控类 (LowMemory / CPU Highload / RSS)**：
    *   **结论**：应用资源使用（内存、CPU）超标，或整机资源不足导致被动回收。
    *   **方向**：需优化应用资源占用，治理内存泄漏和后台行为。

#### 修改建议

根据导出的 FaultLog 文件及定界结果，针对典型场景的修复策略如下：

**场景 1：Reason 为 JsCrash**

*   **定性**：ArkTS/JS 层面的未捕获异常。
*   **分析方法**：打开 jscrash- 开头的日志，搜索 Stacktrace，查找指向项目源码（Entry/Src）的文件名和行号。

**场景 2：Reason 为 CppCrash**

*   **定性**：Native 层（NAPI/So库）的内存违规或段错误。
*   **分析方法**：打开 cppcrash- 开头的日志，查看 Tid（崩溃线程）下的堆栈（#00, #01...）。
    *   若栈顶是应用自带 .so，一般路径为/data开头：应用自身 C++ 代码问题。
    *   若栈顶是系统 .so，一般路径为/system开头（如 libark\_jsruntime.so）：通常为上层传参错误导致系统库崩溃。

**场景 3：Reason 为 APP\_INPUT\_BLOCK / THREAD\_BLOCK\_6S**

*   **定性**：主线程被耗时操作阻塞（ANR）。
*   **分析方法**：打开 appfreeze- 日志，搜索 Name:MainThread，查看其下方的调用栈，找出重复出现或明显耗时的函数调用。

**场景 4：Reason 为 LIFECYCLE\_TIMEOUT**

*   **定性**：UIAbility 生命周期回调执行超时。
*   **分析方法**：确认崩溃发生在应用启动（onCreate）或前后台切换（onForeground）阶段。