为何 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),必须通过焦点状态心跳输入通道代理维持事件流的连续性。

相关推荐
随遇丿而安5 分钟前
第13周:页面状态保存 + 数据恢复优化
android
万事可爱^34 分钟前
Claude 新发布的 Opus 5,系统提示语删了 80%,半价还能逼近 Fable 5
android·服务器·数据库·人工智能·claude
alexhilton1 小时前
响应式的Android身份验证架构
android·kotlin·android jetpack
喵都学不动了2 小时前
Android 自动化测试完全指南(新人版)
android
码云骑士2 小时前
76-全量微调vs-LoRA-vs-QLoRA-三种微调方式对比与选型
android
光头闪亮亮3 小时前
Fyne ( go跨平台GUI )项目实战-WebView 组件开发技术详解
android·go
嵌入式小周3 小时前
Genymotion 安卓模拟器在 Intel 芯片 Mac 上的运行(附带下载方式)
android·macos
zzq77975 小时前
Android 16 API 36 升级后 APP 加固兼容性问题解析
android·开发语言·安全·kotlin·安卓·安全架构
2601_961391466 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·人工智能
2501_9159184115 小时前
深入对比iOS开发中常用性能监控工具的底层原理与优缺点分析
android·ios·小程序·https·uni-app·iphone·webview