为何 FLAG_NOT_FOCUSABLE 导致 ANR

ANR 根本原因:InputDispatcher 超时机制

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
void updateInputWindowsLw(boolean force) {
    // 关键流程:构建可接收输入的窗口列表
    for (WindowState w : mService.mWindowManager.getWindowListLocked()) {
        if (w.canReceiveKeys()) {  // FLAG_NOT_FOCUSABLE 窗口被排除
            addInputWindowHandleLw(handles, w);
        }
    }
    // 将窗口列表同步到 InputDispatcher
    mService.mInputManager.setInputWindows(handles);
}

事件分发核心流程


关键源码路径分析

1. 窗口焦点状态更新 (WindowManagerService)

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
boolean canReceiveKeys() {
    return (mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0; // 关键判断
}

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void setFocusedWindow(WindowState newFocus) {
    if (newFocus == null || !newFocus.canReceiveKeys()) {
        mInputMonitor.setFocusedAppLw(null); // 清除焦点应用
    }
}

2. InputDispatcher 焦点处理 (Native 层)

cpp 复制代码
// frameworks/native/services/inputflinger/InputDispatcher.cpp
bool InputDispatcher::findFocusedWindowTargetsLocked(...) {
    if (mFocusedWindowHandle == nullptr) {
        // ANR 触发点:记录超时原因
        reason = StringPrintf("because there is no focused window");
        return false;
    }
    // ...正常分发流程...
}

3. ANR 检测机制

cpp 复制代码
// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::processAnrsLocked() {
    if (currentTime > mAnrTracker.firstTimeout()) {
        // 生成 ANR 报告
        dispatchUnresponsive = createAnrMessageLocked();
        // 回调到 Java 层
        mPolicy->notifyUnresponsive(dispatchUnresponsive);
    }
}

多模块协同故障链


解决方案:避免 ANR 的架构设计

方案 1:动态焦点控制 + 心跳机制

java 复制代码
// 在 Activity 中实现焦点状态机
private val focusStateMachine = object : FocusStateListener {
    override fun onNeedFocus() {
        window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
        window.decorView.post { 
            window.decorView.requestFocus() 
        }
    }
    
    override fun onCanReleaseFocus() {
        window.setFlags(
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        )
    }
}

// 添加输入事件心跳检测
class InputHeartbeatTracker(context: Context) {
    fun start() {
        Handler(Looper.getMainLooper()).postDelayed(heartbeatRunnable, 1000)
    }
    
    private val heartbeatRunnable = object : Runnable {
        override fun run() {
            if (SystemClock.uptimeMillis() - lastEventTime > 3000) {
                focusStateMachine.onNeedFocus() // 防止长期无焦点
            }
            handler.postDelayed(this, 1000)
        }
    }
}

方案 2:自定义 InputChannel 代理

java 复制代码
// 创建透明输入代理窗口
fun createInputProxyWindow() {
    val params = WindowManager.LayoutParams(
        TYPE_APPLICATION_OVERLAY,
        FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCH_MODAL,
        PixelFormat.TRANSPARENT
    ).apply {
        inputFeatures = INPUT_FEATURE_PROXY_INPUT
    }
    
    val proxyView = View(context).apply {
        setOnKeyListener { _, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                handleBackPressed()
                true
            } else false
        }
    }
    
    windowManager.addView(proxyView, params)
}

方案 3:WMS 层 Hook (需系统权限)

java 复制代码
// 通过 AIDL 定制 WindowManagerPolicy
interface ICustomWindowPolicy : IInterface {
    fun shouldSkipFocusCheck(attrs: WindowManager.LayoutParams): Boolean
}

// 在 PhoneWindowManager 中注入
override fun interceptKeyBeforeQueueing(event: KeyEvent, policyFlags: Int): Int {
    if (customPolicy.shouldSkipFocusCheck(topWindow.attrs)) {
        return ACTION_PASS_TO_USER // 绕过焦点检查
    }
    return super.interceptKeyBeforeQueueing(event, policyFlags)
}

性能优化表:焦点控制策略对比

策略 ANR 风险 系统负担 兼容性 实现复杂度
动态焦点控制 ★☆☆☆☆ API 1+ ★★☆☆☆
InputChannel 代理 ★★☆☆☆ API 26+ ★★★★☆
WMS 层 Hook ☆☆☆☆☆ 需ROOT ★★★★★
透明Activity桥接 ★★★☆☆ API 1+ ★★★☆☆

核心设计原则 :在 Android 输入子系统中,FLAG_NOT_FOCUSABLE 会破坏 WMS(WindowManagerService) 和 InputDispatcher 之间的焦点契约。任何长期处于无焦点窗口的状态都会触发系统的安全保护机制(ANR),必须通过焦点状态心跳输入通道代理维持事件流的连续性。

相关推荐
程序员JerrySUN2 小时前
Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
android·java·linux·运维·开发语言·学习
经典19923 小时前
mysql 性能优化之Explain讲解
android·mysql·性能优化
Kiri霧4 小时前
Kotlin集合与空值
android·开发语言·kotlin
Glacien6 小时前
compose动画从底层基础到顶层高级应用(三)核心API之--Transition
android
亿刀6 小时前
为什么要学习Flutter编译过程
android·flutter
suqingxiao6 小时前
android虚拟机(AVD)报错The emulator process for AVD xxx has terminated
android
whysqwhw6 小时前
OkHttp Cookie 处理机制全解析
android
Evan_ZGYF丶7 小时前
【RK3576】【Android14】ADB工具说明与使用
android·驱动开发·android14·rk3576
幻雨様7 小时前
UE5多人MOBA+GAS 番外篇:移植Lyra的伤害特效(没用GameplayCue,因为我失败了┭┮﹏┭┮)
android·ue5
狂浪天涯8 小时前
Android 16 显示系统 | 从View 到屏幕系列 - 4 | GraphicBuffer & Gralloc
android·操作系统