PS:本文为转载文章,原文可读性会更好,文末有原文链接PS:本文基于AndroidApi26分析源代码。1、Dialog的Window是在哪里创建的?Dialog的Window是在哪里创建的?我们来看一个Dialog的构造方法,就是Dialog(@NonNullContextcontext,@StyleResintthemeResId,booleancreateContextThemeWrapper)方法;看到注释1中的代码没有,它创建了一个Window,而这个Window的实现类是PhoneWindow,它和Activity一样,把PhoneWindow作为自己的Window;嗯,既然有Window,就一定有View显示吧?那我们看看这个Dialog的PhoneWindow是如何加载View的。我们看一下Dialog中加载View的方法之一,setContentView(@LayoutResintlayoutResID);mWindow就是Window,Window的实现类是PhoneWindow,那么我们再往下看PhoneWindow的setContentView(@LayoutResintlayoutResID)方法;看PhoneWindow的setContentView(@LayoutResintlayoutResID)方法没有,Activity的setContentView(@LayoutResintlayoutResID)方法解析View的过程和Dialog的setContentView(@LayoutResintlayoutResID)方法解析View的过程是一样的,所以不是需要分析PhoneWindow的setContentView(@LayoutResintlayoutResID)方法。可以看这篇关于Android中AppCompatActivity的setContentView方法分析的文章来理解。2、我们在平时开发中是否遇到这样的问题:如果Dialog使用的Context不是Activity而是Application,那么就会报错。好吧,为了更好的理解,我们先放一段简单的代码;运行应用程序并发现以下错误;这里报错的原因是没有applicationtoken,而applicationtoken一般只属于Activity,所以这里只用Activity的Context作为Dialog就够了;这里,因为Application的token是空的,而Dialog第一次创建Winow的时候token是空的;我们看一下Dialog的一个构造方法,就是Dialog(@NonNullContextcontext,@StyleResintthemeResId,booleancreateContextThemeWrapper);看注释3中的代码,如果第二个参数为null,则表示Dialog的token为null;Dialog的窗口类型是TYPE_APPLICATION,要求必须是ActivityToken,否则系统会抛出BadTokenException;Dialog是应用窗口类型,Token必须是Activity的Token;我们看注释2中的代码,假设context是Activity,看到getSystemService方法的输入是Context.WINDOW_SERVICE,好了,我们看一下Activity的getSystemService方法;看注释4中的代码,nam是Context.WINDOW_SERVICE,所以返回的是mWindowManager,mWindowManager的实现类是WindowManagerImpl;Activity什么时候设置令牌?答案在Activity的attach方法中;看注释5中的代码,第二个参数是Activity的token,好了,我们再往下看Window的setWindowManager方法;Note6中的代码只是将token保存在Activity的Window中;看Note7中的代码,这里指的是Activity的PhoneWindow;OK,我们再往下看WindowManagerImpl的createLocalWindowManager方法;WindowManagerImpl的createLocalWindowManager方法调用了WindowManagerImpl(Contextcontext,WindowparentWindow)方法;什么是mParentWindow?光看名字就知道肯定是某个Window的父Window,也就是说Activity的Window是某个Window的父Window;Activity的token是什么时候给Dialog的?我们看一下Dialog的show方法;注释9中的代码其实就是显示Dialog的View过程,将mShowing设置为true,表示View正在显示;好了,我们看注释9中代码的实现,mWindowManager就是WindowManagerImpl,我们看addView(@NonNullViewview,@NonNullViewGroup.LayoutParamsparams)方法;Note10中的mParentWindow就是Activity的PhoneWindow(如果mContext是Activity),mGlobal就是WindowManagerGlobal,我们看一下WindowManagerGlobal的addView(Viewview,ViewGroup.LayoutParamsparams,Displaydisplay,Windowparent-Window)方法;看注释11中的代码,parentWindow是Activity的PhoneWindow,所以执行了Window的adjustLayoutParamsForSubWindow(WindowMana-ger.LayoutParamswp)方法;看注释12中的代码,没有,decor本质上就是Activity中PhoneWindow的DecorView,decor得到的是mAttachInfo.mWindowToken,而mAttachInfo.mWindowToken就是ActivityDialog中PhoneWindow的token,所以Activity的token实际上是在Dialog的show过程中赋值给Dialog的。我们不是说Dialog的Window是TYPE_APPLICATION窗口吗?如何证明它是?好,我们再回头看Note8的代码,就是Dialog的PhoneWindow的getAttributes方法,在Window中实现;什么是mWindowAttributes,我们看mWindowAttributes的声明;我们看一下WindowManager.LayoutParams的无参构造方法;见注释13中的代码,Window的默认类型是TYPE_APPLICATION,但是Dialog的Window并没有改变类型,所以Dialog的Window是一个TYPE_APPLICATION类型的窗口。最后,我们看一下Dialog的dismiss方法的实现;见注释14,如果当前线程为主线程,则直接执行dismissDialog方法;见注15,如果不是主线程,则切换到主线程执行;OK,我们继续看Dialog的dismissDialog方法;看注释16的代码,最后从PhoneWindow中删除DecorView就大功告成了。
