稳住,今天是周末。
这是一篇我在wanandroid上的问答转化而来,小缘可以说把Android的窗口机制从源码角度说的非常透彻了,非常值得学习一波。
注意wanandroid上已经有+的问题了,绝对是深入学习Android的宝库,也非常感谢大家的回答,尤其感谢小缘,同时希望问答栏目可以持续的够久,产出足够的内容。
问题:
之前看文章,经常看到一些分析Dialog、PopupWindow的文章,有些文章分析如下:
Dialog有自己独立的Window,而PopupWindow没有,所以PopupWindow可以称之为子窗口,而Dialog不是。
问题来了:
1.这种说法合理吗?
2.在Android中,有没有子窗口的概念呢?如果有到底应该以什么为标准呢?
小缘:
"Dialog有自己独立的Window,而PopupWindow没有,所以PopupWindow可以称之为子窗口,而Dialog不是。"这种说法合理吗?
emmmm,从源码的角度来看,确实是这样定义的。。。
但PopupWindow在系统服务进程那边,还是会有一个对应的WindowState对象的,不能说没有Window,这个我们等下细说。
先来看一张图:
图中列出的都是一些耳熟能详的类:
WindowManagerGlobal里持有同一进程内所有ViewRootImpl的引用。
每个ViewRootImpl都对应着一个Window,当然了这里说的Window并不是指PhoneWindow继承的那个类,指的是AIDL接口IWindow的实现类,在ViewRootImpl中以IBinder的形式存在。
除此之外,ViewRootImpl中还有一个很重要的成员变量mWindowAttributes,它其实是WindowManager的静态内部类LayoutParams,现在列出了其中两个属性,第一个type,就是Window的类型,注意!判断一个ViewRootImpl是否"子窗口",就是根据这个属性来判断的。第二个属性token,可以理解成ViewRootImpl所属容器的token(这个等下也会介绍到)。
如果你经常接触WindowManager,或者做过一些悬浮窗相关的需求,你会知道在调用WindowManager的addView方法时,需要传入一个WindowManager.LayoutParams对象,这个LayoutParams会在addView方法中保存到新创建的ViewRootImpl对象实例里面,同时这个新创建的ViewRootImpl实例也会被add到WindowManagerGlobal的mRoots中。
在Activity启动时,onResume方法回调之后,ActivityThread就会做一次这样的事,即调用WindowManager的addView方法,把PhoneWindow的DecorView添加进去。添加完之后,我们所看到的界面,它的结构大概就是这样的:
刚刚说过,ViewRootImpl.mWindowAttributes.token,保存的是ViewRootImpl所属容器的token,现在能理解了吧?Activity的主Window的容器就是Activity,所以这里它会持有Activity.mToken的引用。
嗯,如果在Activity中show一个Dialog,它的结构会是怎样的呢:
没错,Dialog看上去是跟MainWindow同级别的存在,因为它们的爸爸都是Activity。从源码的角度来看,是因为Dialog在show方法被调用时,它往WindowManager的addView方法传的LayoutParams,type是没有修改过的,默认是TYPE_APPLICATION,官方把这个type定义为"anormalapplicationwindow"。
可能有同学已经想到了,既然把PopupWindow看作是子窗口,那它内部在向WindowManageraddView的时候,肯定是修改过LayoutParams.type的。
是的,PopupWindow所对应的type是TYPE_APPLICATION_PANEL,它就是子窗口的TYPE。源码上的文档注释是这样说的:"Thesewindowsappearontopoftheirattachedwindow"。
既然子窗口是依附在别的窗口上,那对应ViewRootImpl所属容器的token,就不是Activity的token了,而是:
而是它依附的ViewRootImpl里面的mWindow属性!
开头说了,ViewRootImpl.mWindow是以IBinder的形式存在,所以能直接赋值给LayoutParams.token。
这里提一下,Activity提供的OptionsMenu,也是通过PopupWindow来实现的,所以OptionsMenu也是显示在新的窗口上的。
好啦,应用进程这边大概就说这些,但还没完,还有系统服务进程没说呢!
刚刚一开始讲到,无论是不是子窗口,在系统服务进程那边,都会有一个对应的WindowState对象。
emmmm,还是先来熟悉一下相关类的结构吧:
这边有个叫ConfigurationContainer的类,里面有三个抽象方法:获取子元素总数量、获取子元素对象、获取父容器对象。
到了他的实现类WindowContainer,就多了一个叫mChildren的List,很明显它就是用来储存子元素的。
现在列出了2个WindowContainer的子类,一个是WindowToken,另一个叫WindowState,这两个类都指定了泛型类型为WindowState,也就是说,它从WindowContainer中继承的mChildren装的都应该是WindowState的对象了。
WindowToken还有一个我们或多或少都听说过的子类:ActivityRecord,他就是Activity在系统服务进程对应的对象。关于ActivityRecord相关的类,在之前的回答:进一步了解ActivityRecord、TaskRecord、ActivityStack(