关于创建特殊类型的 Window 报 token is null 的问题

描述

我想正常人也不会去用这些类型的 Window Type 吧:TYPE_APPLICATION_ATTACHED_DIALOGTYPE_APPLICATION_PANELTYPE_APPLICATION_SUB_PANEL
然而某个项目中用到的某个库因为某些 bug 需要利用这些特殊类型的 Window 做 workaround…

于是我尝试创建这些特殊类型的 Window:

override fun onStart() {
    super.onStart()
    windowManager.addView(
        bigVideoFrontLayout,
        WindowManager.LayoutParams(
            WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        )
    )
}

给我整了个原地爆炸:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

这就显得非常诡异,Activity 都 start 了你却告诉我它没有在 running ?
另外,假如我把上面那些特殊类型的 Window 换成普通的 TYPE_APPLICATION ,它是能够正常工作的(只不过不符合我的需求)。

那么,这究竟是怎么回事呢?

解决

这东西是真的难以面向搜索引擎编程,用这些特殊类型 Window 的人还是太少了,自己摸索了一下找到了原因和解决方案。记录一下来为你互联网的知识库做一点微小的贡献。

首先,日志真的没骗人,Activity 此时虽然 start 了但还谈不上 running。
这些特殊类型的 Window 依赖于 Activity 本身 attach 的 Window,但是在 onStart() 时 Activity 还没有 attached to Window ,而这就是问题所在,本质上是一个时序问题。

因此,解决它只需要将代码移动到 onAttachedToWindow() 中即可。

override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    windowManager.addView(
        bigVideoFrontLayout,
        WindowManager.LayoutParams(
            WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        )
    )
}

此外,WindowManager.LayoutParams 本身也有个 token 属性,可以把 window.decorView.windowToken 之类的塞给它来方便调试(不过正常工作的时候并不需要给它赋值)。