You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用LifecycleObserver实现onWindowFocusChanged()等效效果?

解决LifecycleObserver中实现类似onWindowFocusChanged的UI操作问题

核心问题原因

onResume() 是Activity生命周期回调,此时Activity已进入可见状态,但窗口(Window)可能尚未完成与系统的绑定、获取焦点,直接在此处执行Window相关操作(如添加悬浮窗、Toast)会因Window Token未就绪触发 WindowManager$BadTokenException。

可行解决方案

方案1:利用主线程消息队列延迟执行(最简单)

在onResume()中通过DecorView.post()将UI操作投递到主线程消息队列尾部,此操作会等待当前生命周期流程、窗口绘制等任务完成后执行,确保窗口已就绪。

class FocusLifecycleObserver(private val activity: Activity) : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        // 投递任务到消息队列尾部,等待窗口就绪
        activity.window.decorView.post {
            // 此处执行你的窗口操作,比如添加悬浮窗
            val overlayParams = WindowManager.LayoutParams().apply {
                type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                } else {
                    WindowManager.LayoutParams.TYPE_PHONE
                }
                flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                width = WindowManager.LayoutParams.WRAP_CONTENT
                height = WindowManager.LayoutParams.WRAP_CONTENT
                gravity = Gravity.CENTER
            }
            val overlayView = LayoutInflater.from(activity).inflate(R.layout.layout_overlay, null)
            activity.windowManager.addView(overlayView, overlayParams)
        }
    }
}

方案2:监听DecorView的附着状态

通过View.OnAttachStateChangeListener监听Activity的根布局(DecorView)是否已绑定到Window,当onViewAttachedToWindow回调触发时,说明窗口已完成绑定,可安全执行UI操作。

class FocusLifecycleObserver(private val activity: Activity) : DefaultLifecycleObserver {
    private val attachListener = object : View.OnAttachStateChangeListener {
        override fun onViewAttachedToWindow(v: View) {
            // DecorView已绑定到Window,窗口就绪
            performWindowOperations()
        }

        override fun onViewDetachedFromWindow(v: View) {
            // 可在此处清理窗口资源
        }
    }

    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        activity.window.decorView.addOnAttachStateChangeListener(attachListener)
    }

    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
        // 移除监听,避免内存泄漏
        activity.window.decorView.removeOnAttachStateChangeListener(attachListener)
    }

    private fun performWindowOperations() {
        // 执行窗口相关UI操作
    }
}

方案3:结合onWindowFocusChanged与LiveData传递状态

如果需要精确监听窗口焦点变化(不仅是首次就绪),可在Activity中重写onWindowFocusChanged,通过LiveData将焦点状态传递给LifecycleObserver。

// 定义焦点状态的LiveData
class WindowFocusState : MutableLiveData<Boolean>()

// Activity中实现焦点回调并发送状态
class MainActivity : AppCompatActivity() {
    val windowFocusState = WindowFocusState()

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            windowFocusState.postValue(true)
        }
    }
}

// LifecycleObserver中观察状态变化
class FocusLifecycleObserver(private val focusState: WindowFocusState) : DefaultLifecycleObserver {
    private val focusObserver = Observer<Boolean> { hasFocus ->
        if (hasFocus == true) {
            performWindowOperations()
        }
    }

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        focusState.observe(owner, focusObserver)
    }

    private fun performWindowOperations() {
        // 执行窗口相关UI操作
    }
}

方案选择建议

  • 仅需在Activity首次获焦时执行一次操作:优先选方案1,代码简洁且足够可靠。
  • 需要监听窗口绑定状态的变化:选方案2。
  • 需要多次响应窗口焦点变化(如Activity从后台切回前台获焦):选方案3。

内容的提问来源于stack exchange,提问作者Akari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:05:21