Android:坑-dialog失焦主动关闭崩溃

dialog失焦主动关闭崩溃

欢迎关注我的代码库:wuxu_future

开发时,有这样一个需求浮窗需要在另一个浮窗出现后,自动关闭,也就是在window失焦时,主动关闭。可是出现了空指针:

java 复制代码
 E  FATAL EXCEPTION: main
Process: com.wuxu.floatwindow.cient2, PID: 29521
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findFocus()' on a null object reference
    at android.view.ViewRootImpl.handleWindowFocusChanged(ViewRootImpl.java:3272)
    at android.view.ViewRootImpl.access$1100(ViewRootImpl.java:191)
    at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:5053)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:949)

解决方案

最终解决方案:使用 postDelayed 解决。

kotlin 复制代码
override fun onWindowFocusChanged(hasFocus: Boolean) {
    super.onWindowFocusChanged(hasFocus)
    if (!hasFocus) {
        // 延迟处理避免焦点切换过程中的竞争条件
        window?.decorView?.postDelayed({
            safeDismiss()
        }, 200)
    }
}

这段 postDelayed 代码的作用是 延迟处理焦点丢失事件,避免窗口状态冲突。具体来说:

关键作用说明

  1. 防竞态条件(Race Condition)

    • 当窗口焦点快速切换时(如用户快速点击其他窗口),立即调用 dismiss() 可能导致:
      • 窗口尚未完成焦点丢失流程
      • 视图树处于中间状态
    • 延迟 200ms 确保系统完成焦点变更的完整周期
  2. 规避异步操作残留

    • 在 onWindowFocusChanged 中直接操作,可能与其他异步任务(如动画、布局计算)产生冲突
    • 延迟到主线程消息队列尾部执行,保证操作顺序性
  3. 视图状态验证窗口期

    • 200ms 的延迟为视图系统提供缓冲时间,可在此期间:
      • 检测是否焦点重新获得(如用户快速切回)
      • 验证 window.decorView 是否仍有效

参数选择依据

延迟时间 适用场景 风险
200ms 通用焦点切换场景 较平衡用户体验与稳定性
更短 (50-100ms) 对响应速度敏感场景 可能残留竞态条件
更长 (300-500ms) 复杂窗口系统(如 TV 大屏) 用户感知延迟

典型应用场景示例

  1. 全局悬浮窗失焦关闭

    当用户点击其他应用窗口时,延迟确保悬浮窗关闭操作不会与系统焦点管理冲突

  2. 游戏悬浮工具栏

    kotlin 复制代码
    // 在游戏暂停菜单中
    override fun onPause() {
        window.decorView.postDelayed(::hideToolbar, 200) 
    }
  3. TV Launcher 焦点管理

    kotlin 复制代码
    // 处理遥控器方向键快速导航
    fun onFocusChange() {
        postDelayed({ updateFocusHighlight() }, 200)
    }

我来讲两句

上面的代码中,postDelayed 的作用是延迟处理焦点丢失事件,避免窗口状态冲突,但AI回答的并不是准确原因。先看源码

  1. 首先是ViewRootImpl.java

    java 复制代码
    private void handleWindowFocusChanged() {
        //...
    
            if (mView != null) {
                mAttachInfo.mKeyDispatchState.reset();
                mView.dispatchWindowFocusChanged(hasWindowFocus);
                mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); // 这里1
                if (mAttachInfo.mTooltipHost != null) {
                    mAttachInfo.mTooltipHost.hideTooltip();
                }
            }
    
            // Note: must be done after the focus change callbacks,
            // so all of the view state is set up correctly.
            mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus, // 这里 2
                    mWindowAttributes);
    
        //-----------------
        //...
    }

    由上就可以看到,1是分发状态,2是报错的地方,findFocus()

  2. 再看dialog.java源码

    java 复制代码
    @UnsupportedAppUsage
    void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }
    
        if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }
    
        try {
            mWindowManager.removeViewImmediate(mDecor); // 这里 3
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;
    
            sendDismissMessage();
        }
    }

    看到3的位置,就能知道,mWindowManager直接移除了mDecor。

  3. 结合上面两块代码,我们已经可以知道,我在dialog的onWindowFocusChanged回调直接调用了dismiss(),后面viewRootImpl的mView.findFocus()为空,所以导致空指针。

相关推荐
应用市场5 小时前
用本机跑的开源音乐模型,给安卓小游戏做 20 秒无缝循环背景音乐
android·python·游戏开发·soundpool·音频处理
企业数字化笔记5 小时前
表单发布前怎么验收?版本引用、字段兼容和历史数据回归
android·数据挖掘·回归
生信分析笔记5 小时前
Nature 正刊 GPN-Star 基因组语言模型
android
渣渣盟5 小时前
Flink 状态管理深度剖析:从 KeyedState 到 RocksDB,状态如何存储、恢复与扩缩容
android·大数据·flink
avi91115 小时前
电影开源Jevdo - 源码介绍(vue版)没有android 和 没有wpf
android·wpf·框架·数据爬虫·meego·电影管理
OxYGC5 小时前
[工程] PHP2Shell 漏洞解析:两条 PHP 内核 RCE,为什么应用层的防护全部失效
android·开发语言·php
拥小抱21 小时前
Compose 知识点
android
文章永久免费只为良心21 小时前
Web应用安全与防护_第三章_日志文件包含_从一道 CTFshow 题目讲透「日志投毒型 LFI → RCE」:原理、时序陷阱与全变种攻防手册
android·网络安全·渗透测试·漏洞危害·漏洞描述·日志投毒·红队指南
AIsoft_868921 小时前
学术讲座录音如何整理?录音APP可行吗
android·iphone
Patrick在香港1 天前
一个斜杠,把毫秒变成十几秒
android·数据库·python·性能优化·正则表达式·回溯·redos