AppFreeze-输入事件阻塞（APP\_INPUT\_BLOCK）故障定位
=======================================

#### 问题现象

用户在操作应用时（如点击按钮、滑动屏幕、按键），应用界面完全无反应，仿佛“冻结”了一般。持续等待约**5秒**后，应用进程被系统终止或弹出“应用无响应”提示框（一般为平板或pc）。

#### 背景知识

HarmonyOS的多模输入服务（Multimodal Input Service）负责分发触摸、按键等事件。当用户触摸屏幕时，输入事件会被发送到应用的主线程（Main Thread）消息队列中等待处理。

系统规定，应用必须在 **5秒** 内对输入事件完成派发和处理。如果主线程被其他耗时任务（如复杂计算、IO操作）占用，导致输入事件在队列中排队等待超过5秒仍未被执行，系统为了防止设备“假死”，会判定为输入阻塞，触发APP\_INPUT\_BLOCK故障，生成AppFreeze日志并终止进程。

#### 问题定位

1.  **获取故障日志**。
    
    通过DevEco Studio的FaultLog面板，或者通过命令hdc file recv /data/log/faultlog/faultlogger/ .导出以appfreeze-开头的日志文件。
    
2.  **分析日志头部信息（判定卡死时长）**。
    
    打开日志，关注Event和Timestamp，确认故障类型为输入阻塞。
    
    Generated by HiviewDFX
    ...
    Event: APP\_INPUT\_BLOCK          <-- 故障类型：输入阻塞
    Timestamp: 2024-05-21 10:00:05  <-- 日志生成时间
    Reason: Input dispatching timed out  <-- 触发原因：输入分发超时
    
3.  **分析消息队列**。
    
    输入事件被阻塞，通常是因为主线程正在执行**另一个**耗时任务，导致无法处理输入事件。需要查看Main handler dump中的Current Running，找出当前在执行的任务。
    
    输入：查看日志中的Current Running字段。
    
    Event: APP\_INPUT\_BLOCK
    Timestamp: 2025/06/28-14:08:30.500      <-- 1. 系统判定超时的时间
    ...
    Main handler dump is:
     EventHandler dump begin curTime: 2025-06-28 14:08:30.200
     Current Running: start at 2025-06-28 14:08:24.100,        <-- 2. 正在执行的任务开始时间
     task name = .ets/pages/Index.ts:onPageShow,               <-- 3. 任务名称（如生命周期函数）
     caller = \[ohos\_loop\_handler.cpp(OnTriggered:72)\]
    
    判定公式：
    
    **阻塞时长=日志生成时间（Timestamp）-当前任务开始时间**。
    
    实际日志计算：
    
    Timestamp(14:08:30.500)-start at(14:08:24.100)≈**6.4秒**。
    
    判断：该任务（onPageShow）执行超过了5秒，导致在此期间用户产生的点击事件无法被响应。
    
4.  **分析主线程堆栈（定位代码行）**。
    
    在日志下方找到Tid与主线程Pid一致的堆栈信息（Name:main）。
    
    *   **如果是UI逻辑卡死**：堆栈通常指向ArkTS源码。
        
        #00 pc 000000000018a2d0 /system/lib64/libark\_runtime.so (ArkJS::Interpreter::Run+200) <-- 纯JS执行
        #01 pc 000000000008b100 /data/storage/el1/bundle/.../entry.hap (Utils.ets:50) <-- 具体的计算逻辑行号
        #02 pc 000000000008c2a0 /data/storage/el1/bundle/.../entry.hap (Index.ets:45) <-- onClick 调用处
        
    *   **如果是同步IPC阻塞**：堆栈指向Binder相关调用。
        
        #00 pc ... libipc\_core.z.so (OHOS::BinderDriver::Transact+...)
        #01 pc ... libaa.z.so (WaitSystemAbility+...)
        
    *   **如果是渲染/布局阻塞**：堆栈可能包含Measure、Layout关键字，提示界面布局过于复杂或在更新UI时进行了错误的计算。

#### 分析结论

APP\_INPUT\_BLOCK与THREAD\_BLOCK\_6S的根本原因高度重合，区别在于**触发时机**：前者是在用户交互时触发，后者是通用检测。

1.  **生命周期耗时**：在aboutToAppear、onPageShow或组件的Build阶段执行了耗时操作（如同步读取文件、解压数据），导致界面虽然显示了但无法响应点击。
2.  **事件回调耗时**：在上一次点击事件的回调（onClick）中执行了长耗时任务，导致后续的点击无法进入。
3.  **主线程死锁/IPC阻塞**：主线程在等待底层资源，处于不可中断的睡眠状态。

#### 修改建议

1.  耗时逻辑迁移至TaskPool（CPU密集型）
    
    如果在onClick或onTouch回调中包含图像处理、大JSON解析、长数组遍历等操作，建议迁移至子线程。
    
2.  I/O与IPC操作全异步化（I/O阻塞型）
    
    文件读写（File）、数据库操作（RDB）、首选项（Preferences）及跨进程通信（IPC）不要在主线程使用同步接口（Sync后缀方法），替换为Promise或AsyncCallback形式的API。
    
3.  UI渲染性能优化（渲染阻塞型）
    
    如果界面布局过于复杂，或者列表数据量过大，会导致构建（Build）和布局（Layout）阶段占用主线程过久，无法及时响应输入。
    
    *   **列表优化**：长列表（List/Grid/WaterFlow）使用 **LazyForEach** 替代ForEach，实现按需加载，避免一次性创建所有组件。
    *   **布局层级**：减少布局嵌套深度，移除冗余的容器组件（如不必要的Stack或Column）。
    *   **状态管理**：使用@State、@Link等装饰器时，控制状态变量关联的UI组件范围，实现**精准刷新**，避免修改一个变量导致整个页面重绘。
4.  高频输入事件节流（逻辑堆积型）
    
    对于onTouch（触摸移动）、onScroll（滚动）、onTextChange（文本变化）等高频触发的回调，如果处理逻辑过重，会瞬间堆积大量任务导致主线程卡死。
    
    *   **节流（Throttle）**：限制回调触发频率（如每16ms响应一次）。
    *   **防抖（Debounce）**：在用户停止操作后再执行最终逻辑。
    *   **避免重逻辑**：在高频回调中仅更新必要的轻量级状态，避免触发复杂的布局计算。
5.  避免主线程锁竞争（死锁/锁等待）
    
    检查主线程是否在等待一把被子线程持有的锁（如AppStorage的读写锁、Native层的std::mutex）。
    
    *   缩小锁的粒度（Scope）。
    *   避免在持有锁的情况下进行I/O操作或IPC调用。
    *   使用AsyncLock（ArkTS侧）防止阻塞主线程。

以下是一些细分场景分类：

| 
#

 | 

细分场景分类

 | 

详细成因与代码行为

 | 

关键堆栈/函数特征

 |
| :-- | :-- | :-- | :-- |
| 

1

 | 

**UI: 复杂列表加载**

 | 

在 List 或 Grid 的 LazyForEach 中，itemGenerator 构建函数逻辑过于复杂，或主线程一次性加载了成千上万条数据。

 | 

OHOS::Ace::Framework::...BuildItem

ArkJS::Interpreter

 |
| 

2

 | 

**LifeCycle: 阻塞初始化**

 | 

在 Ability 的 onCreate 或页面的 aboutToAppear 中同步初始化大型 SDK 或读取大型 JSON 配置文件。

 | 

entry.hap (源码行号)

libark\_runtime.so

 |
| 

3

 | 

**Event: 连点阻塞**

 | 

用户第一次点击触发了耗时任务 A（运行在主线程），此时任务 A 尚未结束，用户再次点击，第二次点击引发 Input Block。

 | 

onClick 对应的匿名函数

 |
| 

4

 | 

**Layout: 嵌套灾难**

 | 

页面布局嵌套层级过深（如 >30层），导致测量和布局计算（Measure/Layout）耗时超过阈值，且发生在用户交互触发刷新时。

 | 

OHOS::Ace::RenderNode::Layout

OHOS::Ace::FlutterRender

 |
| 

5

 | 

**IPC: 等待 Window 服务**

 | 

应用尝试调整窗口大小、亮度或模式，同步调用 WindowManager 接口，但 WindowManagerService 繁忙或卡死。

 | 

OHOS::Rosen::Window::SetBrightness

OHOS::BinderDriver::Transact

 |
| 

6

 | 

**Asset: 同步加载大图**

 | 

在主线程使用 ImageSource.createPixelMap 同步解码 4K/8K 级大图，用于点击后的弹窗展示。

 | 

ImageSource::CreatePixelMap

libimage\_source.z.so

 |
| 

7

 | 

**Logic: 剪切板阻塞**

 | 

用户点击“粘贴”按钮，应用在主线程同步读取系统剪切板大量数据，或等待剪切板服务响应。

 | 

OHOS::Pasteboard::PasteboardClient

 |