之前在我的這篇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 |
一張圖概況下這幾種窗口的區別,來源