Acitivity的Window慎用FLAG_NOT_FOCUSABLE

要深入分析设置 FLAG_NOT_FOCUSABLE 导致输入分发超时(ANR)的原因,需从 Android 输入事件分发的底层机制入手,结合 InputManagerService(IMS)、WindowManagerService(WMS)和应用进程的事件处理逻辑,剖析事件 "卡住" 的根源。

1. 输入事件分发的核心流程与超时机制

Android 系统中,输入事件(触摸、按键等)的分发由 InputManagerService(IMS) 主导,其核心组件 InputDispatcher 负责事件队列管理和超时监控。当输入事件产生后,流程如下:

  1. 事件接收 :Linux 内核捕获输入事件,传递给 IMS 的 InputReader

  2. 事件分发InputReader 将事件转换为 Android 事件对象(MotionEvent/KeyEvent),交给 InputDispatcher

  3. 目标窗口匹配InputDispatcher 通过 WMS 查找最合适的目标窗口(基于窗口可见性、层级、焦点等)。

  4. 事件发送与监控InputDispatcher 将事件通过 InputChannel 发送到目标窗口,并启动超时计时器(默认 5 秒)。

  5. 事件处理反馈 :应用进程处理完事件后,通过 InputChannelInputDispatcher 发送 "处理完成" 信号,计时器终止。

ANR 触发条件 :若事件发送后,InputDispatcher 在 5 秒内未收到 "处理完成" 信号,则判定为 "输入分发超时",触发 ANR,并输出日志 Input dispatching timed out

2. FLAG_NOT_FOCUSABLE 对事件分发的关键影响

设置 FLAG_NOT_FOCUSABLE 后,窗口的属性会发生以下变化(源码验证):

java

typescript 复制代码
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
public boolean canReceiveKeys() {
    // 窗口不可聚焦时,无法接收按键事件
    return !mAttrs.flags.contains(FLAG_NOT_FOCUSABLE) && !mAttrs.flags.contains(FLAG_NOT_TOUCHABLE);
}

public boolean isTouchable() {
    // 触摸事件是否可接收,与 FOCUSABLE 无关,仅由 FLAG_NOT_TOUCHABLE 控制
    return !mAttrs.flags.contains(FLAG_NOT_TOUCHABLE);
}
  • 按键事件:被 WMS 拦截(如前文分析),不会到达应用。

  • 触摸事件 :只要未设置 FLAG_NOT_TOUCHABLE,仍会被 InputDispatcher 分发到窗口(基于坐标命中检测)。

隐患点 :触摸事件虽能到达窗口,但窗口 "不可聚焦" 的状态会破坏事件处理的闭环,导致 InputDispatcher 无法收到 "处理完成" 信号,最终超时。

3. 为何触摸事件会导致超时?

触摸事件到达窗口后,需经过应用进程内的 ViewRootImplDecorViewViewGroupView 层级分发,最终由某个 View 消费(如按钮的 onClick)。若事件未被消费,系统会默认 "处理完成",不会超时。但 FLAG_NOT_FOCUSABLE 会导致以下异常:

3.1 窗口状态矛盾:可触摸但不可交互

FLAG_NOT_FOCUSABLE 的语义是 "窗口不可交互"(无法获取焦点),但触摸事件仍被强制分发到窗口。此时,应用进程可能处于 "矛盾状态":

  • 窗口内的 View 可能因窗口无焦点而无法正常处理事件(例如,依赖焦点的 EditText 无法响应输入,但普通按钮理论上可点击)。
  • 若窗口内所有 View 均未消费触摸事件,事件会 "穿透" 到下层窗口吗?实际不会:InputDispatcher 已将事件分配给当前窗口,下层窗口不会再接收,导致事件 "悬停" 在当前窗口,未被任何组件处理。

3.2 ViewRootImpl 对无焦点窗口的事件处理异常

ViewRootImpl 是应用进程中连接窗口与 View 树的核心类,负责接收 InputChannel 传来的事件并分发到 View 树。当窗口无焦点时,ViewRootImpl 的事件处理逻辑可能异常:

java

csharp 复制代码
// frameworks/base/core/java/android/view/ViewRootImpl.java
public void dispatchInputEvent(InputEvent event) {
    // 检查窗口是否处于"可交互"状态
    if (!mWindowAttributes.flags.contains(FLAG_NOT_FOCUSABLE)) {
        // 正常分发流程
        mInputEventReceiver.dispatchInputEvent(event);
    } else {
        // 无焦点窗口的特殊处理:可能直接丢弃事件,或未正确发送"处理完成"信号
        finishInputEvent(event, false); // 假设此处逻辑错误,未通知 InputDispatcher
    }
}

ViewRootImpl 对无焦点窗口的事件处理存在漏洞(例如,未调用 finishInputEvent 通知 InputDispatcher 事件已处理),InputDispatcher 会一直等待,直至超时触发 ANR。

3.3 系统级事件监控的误判

WMS 维护着 "焦点窗口" 的全局状态,当应用设置 FLAG_NOT_FOCUSABLE 后,WMS 会将该窗口从 "焦点窗口列表" 中移除。此时:

  • InputDispatcher 在分发事件时,发现目标窗口 "无焦点",但事件已被发送,形成 "系统认为窗口不应处理事件,但事件已到达窗口" 的矛盾。
  • 系统可能因 "目标窗口无焦点" 而拒绝承认其事件处理结果,导致 InputDispatcher 始终收不到确认信号,触发超时。

4. 源码验证:ANR 触发的关键判定逻辑

InputDispatcher 中,超时判定的核心代码如下:

java

arduino 复制代码
// frameworks/base/services/core/java/com/android/server/input/InputDispatcher.java
void handleTargetsNotReadyLocked(long currentTime) {
    // 遍历所有未处理的事件
    for (const auto& entry : mInboundQueue) {
        nsecs_t timeout = entry.event->getTimeout();
        if (currentTime >= entry.deliveryTime + timeout) {
            // 事件超时,触发 ANR
            onANRLocked(entry.event, "Input dispatching timed out");
            return;
        }
    }
}

当窗口设置 FLAG_NOT_FOCUSABLE 时,触摸事件的 deliveryTime 被记录,但因未收到 "处理完成" 信号,currentTime 会逐渐超过 deliveryTime + 5000ms(超时阈值),最终触发 onANRLocked 方法,输出错误日志 does not have a focused window(因目标窗口无焦点)。

5. 总结:ANR 的根本原因

设置 FLAG_NOT_FOCUSABLE 后,ANR 产生的核心链为:

  1. 事件分发矛盾 :触摸事件被 InputDispatcher 分发到窗口(因未设置 FLAG_NOT_TOUCHABLE),但窗口无焦点,导致事件无法被正常消费(或消费后未正确反馈)。
  2. 超时监控触发InputDispatcher 等待事件处理结果超过 5 秒,判定为 "应用无响应",触发 ANR。
  3. 日志归因 :系统检测到目标窗口无焦点,故在 ANR 日志中注明 does not have a focused window,指明事件分发失败的根源。
相关推荐
小韩博9 分钟前
小迪之盲注第44课
android·网络安全·adb
夏沫琅琊1 小时前
Android TestDPC 工程详解
android
键来大师2 小时前
Android16 AP热点修改默认密码为12345678
android·framework·rk3576·android16
李坤林2 小时前
Android KGI (Generic Kernel Image)
android
十二测试录2 小时前
Android和iOS测试区别
android·经验分享·ios·职场发展·ab测试
柒许宁安2 小时前
在 Cursor 中运行 Android 项目指南
android·java·个人开发
技术小甜甜2 小时前
【Godot】【入门】GDScript 快速上手(只讲游戏里最常用的 20% 语法)
android·游戏·编辑器·游戏引擎·godot
aqi003 小时前
FFmpeg开发笔记(九十五)国产的开源视频美颜工具VideoEditorForAndroid
android·ffmpeg·音视频·直播·流媒体
sanggou3 小时前
基于Java实现的简易规则引擎(日常开发难点记录)
android·java
farewell-Calm3 小时前
Android Studio 添加第三方库
android·ide·android studio