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()为空,所以导致空指针。

相关推荐
aqi0021 分钟前
FFmpeg开发笔记(六十四)使用国产的RedPlayer播放器观看网络视频
android·ffmpeg·音视频·直播·流媒体
雨白23 分钟前
扩展函数和运算符重载
android
casual_clover2 小时前
Android Studio 解决首次安装时下载 Gradle 慢问题
android·ide·android studio
天天爱吃肉82182 小时前
新能源汽车热管理核心技术解析:冬季续航提升40%的行业方案
android·python·嵌入式硬件·汽车
快乐觉主吖3 小时前
Unity的日志管理类
android·unity·游戏引擎
明月看潮生3 小时前
青少年编程与数学 01-011 系统软件简介 06 Android操作系统
android·青少年编程·操作系统·系统软件·编程与数学
snetlogon203 小时前
JDK17 Http Request 异步处理 源码刨析
android·网络协议·http
消失的旧时光-19434 小时前
Android USB 通信开发
android·java
吃汉堡吃到饱4 小时前
【Android】浅析View.post()
android
咕噜企业签名分发-淼淼4 小时前
开发源码搭建一码双端应用分发平台教程:逐步分析注意事项
android·ios