-
首先创建了一个新的Window,类型是PhoneWindow类型,与Activity创建Window过程类似,并设置
setCallback
回调。 -
将这个新Window与从Activity拿到的
WindowManager
对象相关联,也就是dialog与Activity公用了同一个WindowManager
对象。 -
show方法展示Dialog,先回调了Dialog的
onCreate,onStart
方法。 -
然后获取Dialog自己的
DecorView
对象,并通过addView方法添加到WindowManager对象中,Dialog出现到屏幕上。
分析这个流程我们还可以得知一些平时遇到的小问题,比如为啥Dialog必须要依附于Activity显示?因为Dialog创建过程中需要使用Activity的Context
,即需要使用Activity的token
用来创建window
。所以传入Application的Content就会报错——“Unable to add window -- token null is not for an application”。
回到正题,这个过程用一句话总结就是,Dialog用了Activity的WindowManager对象,并在这之上添加了一个新的Window的DecorView
。
dialog中windowManager的获取
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
如果上述context是Application,实际调用的是ContextImpl.getSystemService,获取的WM是new WindowManagerImpl(display),即mParentWindow为null; 如果上述context是Activity,获取的WM是new WindowManagerImpl(mDisplay, parentWindow),这个parentWindow就是Activity里的PhoneWindow,且这个PhoneWindow设置了mAppToken。
在创建dialog时,如果传入构造方法不是一个activity类型的上下文,则导致WindowManagerImpl类型为Window的变量mParentWindow为null,从而导致WindowManagerGlobal的addView不会调用Window的adjustLayoutParamsForSubWindow方法,从而不会给attr.token赋值,导致在WindowManagerService服务中的身份验证失败,抛出BadTokenException异常。
可以看到只有parentWindow不为空才会走到adjustLayoutParamsForSubWindow方法中,
在adjustLayoutParamsForSubWindow方法中会有对token的保存,只有正确的token才能在后续的跨进程通信过程中完成信息的校验。
Token直译成中文是令牌的意思,android系统中将其作为一种安全机制,其本质是一个Binder对象,在跨进程的通行中充当验证码的作用。比如:在activity的启动过程及界面绘制的过程中会涉及到ActivityManagerService,应用程序,WindowManagerService三个进程间的通信,此时Token在这3个进程中充当一个身份验证的功能,ActivityManagerService与WindowManagerService通过应用程序的activity传过来的Token来分辨到底是控制应用程序的哪个activity。
参考:
https://www.jianshu.com/p/3a1b40f4de36
https://www.jianshu.com/p/413ec659500a