从WmS的角度来观察,一个窗口并不是一个Window类,而是一个View类。当WmS收到用户
的消息后,需要把消息派发到窗口,View类本身并不能直接接收WmS传递过来的消息,真正接收
用户消息的必须是IWindow类,而实现IWindow类的是ViewRoot.W类,每一个W内部都包含了一
个View变量。
WmS并不会介意View是属于哪一个应用程序的,WmS会按照一定的规则判断哪一个View处于活动
状态,然后把用户消息给W类,W类再把用户消息传递给W类内部的View变量,剩余的消息处理就由
View对象去完成了。
WmS判断活动View ->WmS把消息传递给W ->W把消息传递给自己内部的View变量 ->View去处理
Framework定义了三种窗口类型,三种类型的定义在WindowManager类中。
1.第一种为应用窗口。指该窗口对应一个Activity,由于加载Activity是由AmS完成的,因此,对于应用程
序而言,要创建一个应用类窗口,只能在Activity内部完成。
2.第二种是子窗口。指该窗口必须有一个父窗口,父窗口可以是一个应用类型窗口,也可以是任何其他类型
的窗口。
3.第三种是系统窗口。系统窗口不需要对应任何Activity,也不需要有父窗口。对于应用程序而言,理论上是无
法创建系统窗口的,因为所有的应用程序都没有这个权限,然而系统进程却可以创建系统窗口。
WindowManager类对这三种类型进行了细化,把每一种类型都用一个int常量表示,这些实际上代表了
窗口对应的层(Layer)。WmS在进行窗口叠加时,会按照该int常量的大小分配不同层,int值越大,代表层
的位置越靠上面,即所谓的z-order。
这里要讲解一下z-order 。
z-order:Z 和 Order。 在Z坐标轴上的排列顺序,即物件在垂直的 Z 坐标轴上的上下位置关系。
z:三度空间里的 Z 坐标轴。
order:排列顺序。
我们来参考一下WindowManager源代码:
/** * Start of window types that represent normal application windows. */ public static final int FIRST_APPLICATION_WINDOW = 1; /** * Window type: an application window that serves as the "base" window * of the overall application; all other application windows will * appear on top of it. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_BASE_APPLICATION = 1; /** * Window type: a normal application window. The {@link #token} must be * an Activity token identifying who the window belongs to. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_APPLICATION = 2; /** * Window type: special application window that is displayed while the * application is starting. Not for use by applications themselves; * this is used by the system to display something until the * application can show its own windows. * In multiuser systems shows on all users' windows. */ public static final int TYPE_APPLICATION_STARTING = 3; /** * End of types of application windows. */ public static final int LAST_APPLICATION_WINDOW = 99;
定义 | 意义 |
FIRST_APPLICATION_WINDOW = 1 |
第一个应用窗口 |
TYPE_BASE_APPLICATION = 1 |
第一个应用窗口 |
TYPE_APPLICATION = 2 |
所有Activity对应的窗口 |
TYPE_APPLICATION_STARTING = 3 |
Activity启动时,可以指定一个启动窗口首先显示,直到真正的Activity窗口配置好后,删除该启动窗口,显示Activity窗口 |
LAST_APPLICATION_WINDOW = 99 |
最后一个应用窗口 |
所以的Activity默认的窗口类型都是TYPE_APPLICATION,WmS在进行窗口叠加时,会动态改变应用窗口的
层值,但层值不会大于99。
WindowManager源码部分:
/** * Start of types of sub-windows. The {@link #token} of these windows * must be set to the window they are attached to. These types of * windows are kept next to their attached window in Z-order, and their * coordinate space is relative to their attached window. */ public static final int FIRST_SUB_WINDOW = 1000; /** * Window type: a panel on top of an application window. These windows * appear on top of their attached window. */ public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; /** * Window type: window for showing media (such as video). These windows * are displayed behind their attached window. */ public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1; /** * Window type: a sub-panel on top of an application window. These * windows are displayed on top their attached window and any * {@link #TYPE_APPLICATION_PANEL} panels. */ public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2; /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout * of the window happens as that of a top-level window, <em>not</em> * as a child of its container. */ public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3; /** * Window type: window for showing overlays on top of media windows. * These windows are displayed between TYPE_APPLICATION_MEDIA and the * application window. They should be translucent to be useful. This * is a big ugly hack so: * @hide */ public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4; /** * End of types of sub-windows. */ public static final int LAST_SUB_WINDOW = 1999;
定义 | 意义 |
FIRST_SUB_WINDOW = 1000 |
第一个子窗口 |
TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW |
应用窗口的子窗口, PopupWindow的默认类型 |
TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1 |
尚未使用 |
TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2 |
尚未使用 |
TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3 |
OptionMenu、ContextMenu 的默认类型 |
TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4 |
尚未使用 |
LAST_SUB_WINDOW = 1999 |
最后一个子窗口 |
创建子窗口时,客户端可以指定窗口类型介于1000〜1999之间,而WmS在进行窗口叠加时,会动态调整层值。
WindowManager源码部分:
/** * Start of system-specific window types. These are not normally * created by applications. */ public static final int FIRST_SYSTEM_WINDOW = 2000; /** * Window type: the status bar. There can be only one status bar * window; it is placed at the top of the screen, and all other * windows are shifted down so they are below it. * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; /** * Window type: the search bar. There can be only one search bar * window; it is placed at the top of the screen. * In multiuser systems shows on all users' windows. */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; /** * Window type: phone. These are non-application windows providing * user interaction with the phone (in particular incoming calls). * These windows are normally placed above all applications, but behind * the status bar. * In multiuser systems shows on all users' windows. */ public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; /** * Window type: system window, such as low power alert. These windows * are always on top of application windows. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; /** * Window type: keyguard window. * In multiuser systems shows on all users' windows. * @removed */ public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; /** * Window type: transient notifications. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; /** * Window type: system overlay windows, which need to be displayed * on top of everything else. These windows must not take input * focus, or they will interfere with the keyguard. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; /** * Window type: priority phone UI, which needs to be displayed even if * the keyguard is active. These windows must not take input * focus, or they will interfere with the keyguard. * In multiuser systems shows on all users' windows. */ public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; /** * Window type: panel that slides out from the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; /** * Window type: dialogs that the keyguard shows * In multiuser systems shows on all users' windows. */ public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; /** * Window type: internal system error windows, appear on top of * everything they can. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; /** * Window type: internal input methods windows, which appear above * the normal UI. Application windows may be resized or panned to keep * the input focus visible while this window is displayed. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11; /** * Window type: internal input methods dialog windows, which appear above * the current input method window. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12; /** * Window type: wallpaper window, placed behind any window that wants * to sit on top of the wallpaper. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; /** * Window type: panel that slides out from over the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999;
定义 | 意义 |
FIRST_SYSTEM_WINDOW = 2000 |
第一个系统窗口 |
TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW |
状态条窗口 |
TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1 |
搜索条窗口 |
TYPE_PHONE = FIRST_SYSTEM_WINDOW+2 |
来电显示窗口 |
TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3 |
警告对话框 |
TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4 |
屏保 |
TYPE_TOAST = FIRST_SYSTEM_WINDOW+5 |
Toast对应的窗口 |
TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6 |
尚未使用 |
TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7 |
在屏幕保护下的 来电显示窗口 |
TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8 |
似乎等同于滑动状态条后出现的窗口 |
TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9 |
屏保弹出的对话框 |
TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10 |
系统错误窗口 |
TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11 |
输入法窗口 |
TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12 |
输入法中备选框对应的窗口 |
TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13 |
墙纸对应的窗口 |
TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14 |
滑动状态条后出现的窗口 |
LAST_SYSTEM_WINDOW = 2999 |
最后一个系统窗口 |
当具备创建系统窗口权限时,创建系统窗口可以指定层值在2000〜2999之间,WmS在进行窗口叠
加时,会动态调整该层值。所不同的是,由于有些系统窗口只能出现一个,即不能添加多个,否则
用户会觉得很乱,比如输入法窗口,再比如系统状态条窗口,因此,WmS在接收到创建窗口的消息
时,会进行一定的检查,确保该窗口只能被创建一次。
token的含义为象征、符号、代表等。在创建窗口时,多处定义了和token相关的变量,而无论该
变量具体的名称是什么,该变量的类型一般都是一个IBinder对象。既然是IBinder对象,其作用也
是显而易见的,即为了进行IPC调用。而与创建窗口相关的IPC对象一般只有两种,一种是指向某个
W类的token,另一种是指向某个HistoryRecord的token。其中HistoryRecord对象是AmS内部
为运行的每一个Activity创建的一个Binder对象,客户端的Activity可以通过该Binder对象通知当前
Activity的状态。
位置 | 定义 |
Activity | IBinder mToken |
Window | IBinder mAppToken |
WindowManager.LayoutParams | IBinder token |
ViewRoot |
View.AttachInfo mAttachlnfo; |
View | View.AttachInfo mAttachlnfo; |
View.AttachInfo |
IBinder mWindowToken; IBinder mPanelParentWindowToken; IWindow mWindow; |
AmS内部为每一个运行的Activity都创建一个IBinder对象,该对象的的名字定义为mToken。该变量的值
是在Activity.attach()函数中完成的。
每一个Window对象中都有一个mAppToken变量,注意这里说的是Window对象,而不是窗口。开
头就提过,一个窗口本质上是一个View,而Window类却是一个应用窗口的抽象,这就好比Window
侧重于一个窗口的交互,而窗口(View)则侧重于窗口的显示。所以,mAppToken并不是W 类的引用,事
实上正如其名称所指,它是AmS在远程为每一个Activity创建的IBinder的引用。
事实上,Window类中还有其他的Binder对象,同时由于Window并不一定要对应一个Activity,因
此,如果Window类不属于某个Activity,mAppToken的变量则为空,否则mAppToken的值与Activity
中的mToken值是相同的。
WindowManager.LayoutParams中token的意义正如其所在的类的名称,该类是在添加窗口
时指定该窗口的布局参数,而token的意义正是指定该窗口对应的Binder对象,因为WmS需要
该Binder对象,以便对客户端进行IPC调用。
该token变量的值可以有三种:
1.如果创建的窗口是应用窗口,token的值和Window中mAppToken值相同。
2.如果创建的窗口为子窗口,token为其父窗口的W 对象。
3.如果创建的窗口是系统窗口,token值为空。
ViewRoot,客户端的每一个窗口都对应一个ViewRoot对象,该对象内部的mAttachlnfo是该对
象被构造时同时创建的。该变量的类型和View对象中的mAttachlnfo相同。
View类中的mAttachlnfo,其含义是当该View对象被真正作为某个窗口 W 类的内部View时,该
变量就会被赋值为ViewRoot中的mAttachlnfo。在一般情况下,屏幕上所有的View对象的mAttachlnfo
都是被赋值过的,因为当W类中的View被添加为一个真正的窗口后,ViewRoot会调用performTraversal()
方法,而该方法则会调用View或者ViewGroup的dispatchToWindow()方法。在后者调用中,会把ViewRoot
中的mAttachlnfo赋值给View中的mAttachlnfo,所以,同一个窗口中包含的所有View对象,其内部的
mAttachlnfo的内容都是相同的。
ViewRootImpl构造时初始化mAttachInfo -> W中的View被添加为真正的窗口后 ->
ViewRootImpl调用performTraversal() -> 调用View或者ViewGroup的dispatchToWindow()方法 ->
ViewRoot中的mAttachlnfo赋值给View中的mAttachlnfo
mAttachlnfo变量中,包含了三个Binder变量。
1.mWindowToken,指的是该窗口对应的W 对象。
2.mPanelParentWindowToken, 如果该窗口是子窗口的,那么该变量即为父窗口中的W 对
象。该变量赋值和mWindowToken是互斥的,因为mWindowToken如果不为空,则意味着该
窗口没有父窗口。
3.mWindow,注意,也是一个Binder对象,但它却更是一个IWindow对象,关于IBinder
由此也可以看出,mWindowToken似乎是多余的,因为mWindowToken=IWindow.asBinder()
只是多了一个变量而已,使用起来更方便。