Android的窗口系統(tǒng)是UI架構(gòu)很重要的一部分,數(shù)據(jù)結(jié)構(gòu)比較多,細(xì)節(jié)比較多。本篇文章主要介紹窗口相關(guān)數(shù)據(jù)結(jié)構(gòu)和抽象概念理解,關(guān)于[窗口部分的博客]計(jì)劃如下。
1、窗口Z-Order的管理
2、應(yīng)用程序和WMS的聯(lián)系
3、窗口的添加,WindowState的創(chuàng)建
4、Token管理,AppToken
5、窗口大小的計(jì)算
6、啟動(dòng)窗口
7、窗口的切換和動(dòng)畫
8、多窗口,分屏、畫中畫,自由模式的實(shí)現(xiàn)思路
9、View的測量和布局
10、桌面啟動(dòng)流程
11、墻紙流程
本篇文章主要討論窗口類型、坐標(biāo)系統(tǒng)、Z-Order,Z-Order確定跟窗口類型有關(guān)系,先看窗口類型。
一、窗口類型
1.1、Window的類型
Android系統(tǒng)的Window有很多個(gè),大體上來說,F(xiàn)ramework定義了三種窗口類型;
系統(tǒng)Window
常見的系統(tǒng)Window有哪些呢?比如在手機(jī)電量低的時(shí)候,會(huì)有一個(gè)提示電量低的Window,我們輸入文字的時(shí)候,會(huì)彈出輸入法Window,還有搜索條Window,來電顯示W(wǎng)indow,Toast對(duì)應(yīng)的Window,可以總結(jié)出來,系統(tǒng)Window是獨(dú)立與我們的應(yīng)用程序的,對(duì)于應(yīng)用程序而言,我們理論上是無法創(chuàng)建系統(tǒng)Window,因?yàn)闆]有權(quán)限,這個(gè)權(quán)限只有系統(tǒng)進(jìn)程有。
應(yīng)用程序Window
所謂應(yīng)用窗口指的就是該窗口對(duì)應(yīng)一個(gè)Activity,因此,要?jiǎng)?chuàng)建應(yīng)用窗口就必須在Activity中完成了。本節(jié)后面會(huì)分析Activity對(duì)應(yīng)的Window的創(chuàng)建過程。
子Window
所謂的子Window,是說這個(gè)Window必須要有一個(gè)父窗體,比如PopWindow,Dialog是屬于應(yīng)用程序Window,這個(gè)比較特殊。
每一種窗口類型定義了一種對(duì)應(yīng)的type
應(yīng)用類型的窗口的type范圍是1~99

子窗口的type范圍是1000~1999

系統(tǒng)的窗口的type范圍是2000以上

系統(tǒng)窗口的type值>子窗口的type值>應(yīng)用類型窗口的type值,一般來說,根據(jù)type值大小關(guān)系,可以推出系統(tǒng)窗口在子窗口的上面,子窗口在應(yīng)用窗口的上面。
二、窗口布局和Z序
窗口的布局一般有兩種,一種是平鋪式的布局,一種是層疊式的布局

平鋪式的布局設(shè)計(jì)簡單,現(xiàn)在還有一些終端系統(tǒng)仍然是單窗口的平鋪布局,手機(jī)上采用的是層疊式布局,層疊式布局是一個(gè)三維的空間,將手機(jī)的水平方向作為X軸,豎直方向作為Y軸,還有一根垂直與屏幕從里朝外方向的虛擬的Z軸,所有窗口 (WindowState) 按照順序排列在Z軸上,如下圖。

2.1、窗口主序的確定
WindowState是WMS中事實(shí)的窗口,而不是Window,WindowState是在WMS的addWindow方法中創(chuàng)建,包含了一個(gè)窗口的所有的屬性,其中一個(gè)屬性為mLayer,表示窗口在Z軸的位置,mLayer值越小,窗口越靠后,mLayer值越大,窗口越靠前,最前面的一個(gè)窗口就作為焦點(diǎn)窗口,可以接收觸摸事件。因?yàn)榇翱诘那袚Q,切換后的Z序(窗口的顯示次序稱為 Z 序)就可能不同,所以mLayer的值不是固定不變的。mLayer是通過WindowState的另一個(gè)成員變量mBaseLayer的值計(jì)算得到,mBaseLayer的值是固定不變的,只和窗口類型有關(guān)。mBaseLayer(稱為主序)是WindowState的構(gòu)造方法中賦值。
mBaseLayer = mPolicy.windowTypeToLayerLw(
attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
等價(jià)與:mBaseLayer =窗口類型×10000+1000
windowTypeToLayerLw根據(jù)窗口的類型type,返回2開始的整數(shù)值
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return 2;
}
switch (type) {
case TYPE_WALLPAPER:
return 2;
// wallpaper is at the bottom, though the window manager may move it.
case TYPE_PHONE:
return 3;
.....
case TYPE_TOAST:
// toasts and the plugged-in battery thing
return 8;
.....
case TYPE_BOOT_PROGRESS:
return 30;
case TYPE_POINTER:
// the (mouse) pointer layer
return 31;
}
Log.e(TAG, "Unknown window type: " + type);
return 2;
可以看到系統(tǒng)中的窗口種類比較多,case個(gè)數(shù)都到31了。比如應(yīng)用程序的窗口是2,電話類型的窗口是3,Toast窗口是8...,因?yàn)橄到y(tǒng)中同類型的窗口比較多,所以對(duì)返回的整數(shù)值乘以10000(WindowManagerService.TYPE_LAYER_MULTIPLIER),在加上1000(WindowManagerService.TYPE_LAYER_OFFSET),相當(dāng)把Z軸劃分成了31個(gè)值域,不同類型的窗口的Z軸位置都是處于兩個(gè)不相交的值域之中,互相不打擾。OK,通過mBaseLayer,我們知道了窗口是如何排序的,一個(gè)窗口有可能需要依附在一個(gè)父窗口上,作為一個(gè)子窗口,所以除了主序的概念外,還有子序。
2.2、窗口子序的確定
SubLayer(稱為子序),SubLayer值是用來描述一個(gè)窗口是否屬于另外一個(gè)窗口的子窗口,或者說SubLayer值是用來確定子窗口和父窗口之間的相對(duì)位置的。

一個(gè)Activity中有三個(gè)子窗口WindowState1、WindowState2、WindowState3,WindowState1WindowState2在窗口A的前面,WindowState3在A的后面,這幾個(gè)兄弟窗口為什么可以這樣排序呢,這就是mSubLayer的作用,子序越大,則相對(duì)其他兄弟窗口越靠前,反之,越靠后,如果為負(fù)數(shù),就處在父窗口的后面,如窗口A中的WindowState3,子序是根據(jù)窗口類型調(diào)用subWindowTypeToLayerLw確定的,subWindowTypeToLayerLw同樣是在Window的構(gòu)造方法中調(diào)用的。
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
public int subWindowTypeToLayerLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER;//返回值是1
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER;//返回值是-2
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;//返回值是-1
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;//返回值是2
case TYPE_APPLICATION_ABOVE_SUB_PANEL:
return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;//返回值是3
}
Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
}
相對(duì)與主序的確定,子序是非常簡單了,但是,假設(shè)系統(tǒng)中有個(gè)應(yīng)用程序現(xiàn)在有5個(gè)窗口,全部都是應(yīng)用類型的窗口,按照上面的規(guī)則,計(jì)算出來的主序應(yīng)該相同,如何排序? 這就需要WMS進(jìn)行后期的調(diào)整。
2.3、窗口Z序的調(diào)整
當(dāng)WindowState創(chuàng)建完成,并且被添加到WMS維持的數(shù)組里面后,就需要調(diào)用WindowLayersController的assignLayersLocked(windows),進(jìn)行Z序的調(diào)整。
//參數(shù)windows是窗口列表
final void assignLayersLocked(WindowList windows) {
if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
new RuntimeException("here").fillInStackTrace());
clear();
int curBaseLayer = 0;
int curLayer = 0;
boolean anyLayerChanged = false;
//遍歷窗口列表,上面通過Z序的計(jì)算公式計(jì)算出來的Z序值保存在WindowState的變量mBaseLayer
中,這個(gè)循環(huán)的意思是,遇到同類型的窗口,后一個(gè)窗口在前一個(gè)窗口的基礎(chǔ)上偏移5。
for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
final WindowState w = windows.get(i);
boolean layerChanged = false;
int oldLayer = w.mLayer;
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
}
// 更新該窗口的mAnimLayer,也就是動(dòng)畫顯示時(shí),該窗口的層級(jí)
assignAnimLayer(w, curLayer);
// TODO: Preserved old behavior of code here but not sure comparing
// oldLayer to mAnimLayer and mLayer makes sense...though the
// worst case would be unintentionalp layer reassignment.
if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
}
// 將當(dāng)前應(yīng)用窗口的最高顯示層級(jí)記錄在mHighestApplicationLayer中
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
w.mWinAnimator.mAnimLayer);
}
// 對(duì)于分屏等相關(guān)的窗口,它們的顯示層級(jí)需要再次處理
collectSpecialWindows(w);
if (layerChanged) {
w.scheduleAnimationIfDimming();
}
}
// 調(diào)整特殊窗口的層級(jí)
adjustSpecialWindows();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && anyLayerChanged
&& windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowLayersChangedLocked();
}
if (DEBUG_LAYERS) logDebugLayers(windows);
}
經(jīng)過調(diào)整之后,同類型的窗口的Z序值就不同了。
3、窗口Z序的深入理解
1、Layer值為何乘以1萬?
Layer的計(jì)算公式為:
mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + WindowManagerService.TYPE_LAYER_OFFSET;
等價(jià)與:mBaseLayer =窗口類型×10000+1000
其中TYPE_LAYER_MULTIPLIER和TYPE_LAYER_OFFSET在WMS的聲明如下,
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET.
*/
static final int TYPE_LAYER_MULTIPLIER = 10000;
/** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
* or below others in the same layer.
*/
static final int TYPE_LAYER_OFFSET = 1000;
mBaseLayer的值是在WindowState的構(gòu)造中計(jì)算的,子窗口和一般窗口計(jì)算有所不同
if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) {//如果是子窗口
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
//使用依附窗口的類型
mBaseLayer = mPolicy.windowTypeToLayerLw(attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);//計(jì)算mSubLayer
......
} else {//非子窗口
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = 0;
......
}
Layer值為什么要乘以10000萬呢?PhoneWindowManager對(duì)象mPolicy的成員函數(shù)windowTypeToLayerLw的返回值并且不是一個(gè)窗口的最終的BaseLayer值,而是要將它的返回值乘以一個(gè)常量TYPE_LAYER_MULTIPLIER(10000),再加上另外一個(gè)常量TYPE_LAYER_OFFSET(1000)之后,才得到最終的BaseLayer值。這是因?yàn)橄嗤愋偷拇翱诘腪軸位置都是有著相同的值域,而不同類型的窗口的Z軸位置都是處于兩個(gè)不相交的值域。例如,假設(shè)有兩種不同類型的窗口,它們的Z軸位置的值域分別為[a, b]和[c, d],那么[a, b]和[c, d]的交集一定等于空。又由于每一種類型的窗口的數(shù)量是不確定的,因此,WindowManagerService服務(wù)就需要為每一種類型的窗口都預(yù)留一個(gè)范圍足夠大的值域,以便可以滿足要求。
2、Z序調(diào)整為何加5而不加其他數(shù)字
Z序的調(diào)整是在WindowLayersController的assignLayersLocked方法實(shí)現(xiàn)的。所謂的調(diào)整就是遍歷窗口列表,如果窗口的mBaseLayer和前一個(gè)相同、或者是輸入法和壁紙窗口就加上常量值WINDOW_LAYER_MULTIPLIER。
private final void assignLayersLocked(WindowList windows) {
int N = windows.size();
int curBaseLayer = 0;
int curLayer = 0;
int i;
boolean anyLayerChanged = false;
for (i=0; i<N; i++) {
final WindowState w = windows.get(i);
final WindowStateAnimator winAnimator = w.mWinAnimator;
boolean layerChanged = false;
int oldLayer = w.mLayer;
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
w.mLayer = curLayer;
}
.......
}
.......
}
WINDOW_LAYER_MULTIPLIER在WMS的聲明如下
/** How much to increment the layer for each window, to reserve room
* for effect surfaces between them.
*/
static final int WINDOW_LAYER_MULTIPLIER = 5;
意思為在每個(gè)窗口層之間預(yù)留空間。加上5之后,在同層的窗口中每隔一個(gè)窗口就留下4個(gè)空位, 如果加上1,按道理來說也行,為了搞清楚為什么加5,我給google維護(hù)窗口系統(tǒng)的工程師發(fā)了一封郵件。

但是很遺憾,一直沒有收到回信,但是應(yīng)該可以確定,這里加1也是可以的,同樣可以使得同類型的窗口的Z序不同,WINDOW_LAYER_MULTIPLIER這個(gè)值從Android2.3版本就是等于5,所以這里WINDOW_LAYER_MULTIPLIER為5并沒有什么特殊的含義。
3、Layer值為何乘以1萬以后為什么還要加上1000?
窗口在打開或者關(guān)閉的時(shí)候,為了不那么突兀,都會(huì)設(shè)置一個(gè)動(dòng)畫,
/** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
* or below others in the same layer. */
static final int TYPE_LAYER_OFFSET = 1000;
public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
int stackClip) {
....
animation = anim;
// 獲得zorder類型,這個(gè)類型是通過Animaion.java的setZAdjustment設(shè)置的
int zorder = anim.getZAdjustment();
int adj = 0;
// ZORDER_TOP則會(huì)+1000,ZORDER_BOTTOM則會(huì)-1000
if (zorder == Animation.ZORDER_TOP) {
adj = TYPE_LAYER_OFFSET;
} else if (zorder == Animation.ZORDER_BOTTOM) {
adj = -TYPE_LAYER_OFFSET;
}
if (animLayerAdjustment != adj) {
animLayerAdjustment = adj;
updateLayers();
}
....
}
所以Z序號(hào)設(shè)置1000的偏移量就是為了這個(gè)動(dòng)畫層級(jí)屬性預(yù)留的空間。