之前在我的这篇blog中写到了Activity启动时activity / window /view之间的关系。大概的流程是,先创建activity,然后attach中执行window创建,接着setContentView中创建DecorView并且将DecorView和window进行绑定,接着在resume过程中通过将decorView添加到windowManager中WindowManager.addView(decor),在addView()方法中会创建ViewRootImpl供wm管理View树,在addView()的末尾会调用root.addView(decorView)最终将界面呈现出来。
Activity创建窗口显示过程
Dialog窗口添加流程
- dialog创建
- new Context
- new Window(顺带创建了DecorView)
- dialog.show()
- dispatchOncreate()
- onStart()
- WM.addView()后面和Activity一样
另外,这里有一批总结性的文章可以参考下
https://www.jianshu.com/p/a50cbdeb1880
Android窗口类型
除了上面的Activity / Dialog(包括AlertDialog)外,Android还为我们提供了PopUpWindow,Toast等形式的弹框,这些弹框最后的展示都是通过调用wm.addView()来展示的。我们也可以自定义弹框,前面创建View,定义好窗口的LayoutParams,设置好窗口type,最后也可以通过wm.addView()来展示窗口。在这些窗口展示过程过,我们可能会遇到窗口添加失败的场景,这些失败在ViewRootImpl的setView()方法中都抛出了对应异常,提供给开发者debug信息。如下图所示:
分析上述常见错误之前,先来了解下android wm管理的窗口类型。Android将窗口分为三类:
- Application window (应用窗口类型) 1~99
- Sub-window(子窗口) 1000~1999
- System window(系统窗口) 2000~2999
这三类窗口按照优先级从大到小,type越大,表示权限越高,能覆盖在其他type之上。
结合上面两张图,可以找到我们平时遇到的window添加失败的原因。
- 很多时候activity消失后弹框就会出现no token的情况,这个时候的做法是,dialog.show()之前判断activity是否已经或者正在finish(),如果是,此时不要弹出dialog(或者toast),避免crash。
- 自定义全局弹窗时,弹框与activity token无关,可以将dialog弹框type设置为系统弹框,如TYPE_APPLICATION_OVERLAY(6.0之后)/SYSTEM_ALERT_WINDOW(6.0之前),同时弹框之前需要动态请求SYSTEM_ALERT_WINDOW权限,否则会爆出权限错误。
窗口类型对应关系
窗口 | 类型 |
---|---|
Activity | TYPE_APPLICATION |
Dialog | TYPE_APPLICATION |
PopUpWindow | TYPE_APPLICATION_PANEL |
Toast | TYPE_TOAST |
一张图概况下这几种窗口的区别,来源