Android P WMS(4) -- removewindow
Android P WMS(5) -- relayoutWindow
Android P WMS(6) -- windowanimator
Android P WMS(7) --wms 問題種類和debug技巧
Android P WMS(8) --View SYstem 簡介
1.WMS概述
WMS是系統的其他服務,無論對於應用開發還是Framework開發都是重點的知識,它的職責有很多,主要有以下幾點:
窗口管理
WMS是窗口的管理者,它負責窗口的啓動、添加和刪除,另外窗口的大小和層級也是由WMS進行管理的。窗口管理的核心成員有DisplayContent、WindowToken和WindowState。
窗口動畫
窗口間進行切換時,使用窗口動畫可以顯得更炫一些,窗口動畫由WMS的動畫子系統來負責,動畫子系統的管理者爲WindowAnimator。
輸入系統的中轉站
通過對窗口的觸摸從而產生觸摸事件,InputManagerService(IMS)會對觸摸事件進行處理,它會尋找一個最合適的窗口來處理觸摸反饋信息,WMS是窗口的管理者,因此,WMS“理所應當”的成爲了輸入系統的中轉站。
Surface管理
窗口並不具備有繪製的功能,因此每個窗口都需要有一塊Surface來供自己繪製。爲每個窗口分配Surface是由WMS來完成的。
WMS的職責可以簡單總結爲下圖。
2.windowState
windowState就是window,每個window都有一個surface來繪畫,window本身是沒法畫的。
Window的類型
Android系統的Window有很多個,大體上來說,Framework定義了三種窗口類型;
系統Window
常見的系統Window有哪些呢?比如在手機電量低的時候,會有一個提示電量低的Window,我們輸入文字的時候,會彈出輸入法Window,還有搜索條Window,來電顯示Window,Toast對應的Window,可以總結出來,系統Window是獨立與我們的應用程序的,對於應用程序而言,我們理論上是無法創建系統Window,因爲沒有權限,這個權限只有系統進程有。
應用程序Window
所謂應用窗口指的就是該窗口對應一個Activity,因此,要創建應用窗口就必須在Activity中完成了。本節後面會分析Activity對應的Window的創建過程。
子Window
所謂的子Window,是說這個Window必須要有一個父窗體,比如PopWindow,Dialog是屬於應用程序Window,這個比較特殊。
每一種窗口類型定義了一種對應的type
應用類型的窗口的type範圍是1~99
應用類型的窗口
子窗口的type範圍是1000~1999
子窗口
系統的窗口的type範圍是2000以上
系統的窗口
系統窗口的type值>子窗口的type值>應用類型窗口的type值,一般來說,根據type值大小關係,可以推出系統窗口在子窗口的上面,子窗口在應用窗口的上面。
3.z-order
手機上採用的是層疊式佈局,層疊式佈局是一個三維的空間,將手機的水平方向作爲X軸,豎直方向作爲Y軸,還有一根垂直與屏幕從裏朝外方向的虛擬的Z軸,所有窗口 (WindowState) 按照順序排列在Z軸上,如下圖。z軸座標就是z-order,越大證明他的windowState越在前面。
3.1 、 mBaseLayer 主序 確認
@/frameworks/base/services/core/java/com/android/server/wm/WindowState.java
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
...
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.getWindowLayerLw(parentWindow //mBaseLayer 主序
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; ////TYPE_LAYER_MULTIPLIER = 10000 TYPE_LAYER_OFFSET = 1000
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type); //mSubLayer 次序
mIsChildWindow = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
|| parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
...
}
mBaseLayer =窗口類型×10000+1000,窗口類型判斷如下
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}
switch (type) {
case TYPE_WALLPAPER:
// wallpaper is at the bottom, though the window manager may move it.
return 1;
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
return APPLICATION_LAYER;
case TYPE_DOCK_DIVIDER:
return APPLICATION_LAYER;
case TYPE_QS_DIALOG:
return APPLICATION_LAYER;
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
case TYPE_VOICE_INTERACTION:
// voice interaction layer is almost immediately above apps.
return 5;
case TYPE_INPUT_CONSUMER:
return 6;
case TYPE_SYSTEM_DIALOG:
return 7;
case TYPE_TOAST:
// toasts and the plugged-in battery thing
return 8;
case TYPE_PRIORITY_PHONE:
// SIM errors and unlock. Not sure if this really should be in a high layer.
return 9;
case TYPE_SYSTEM_ALERT:
// like the ANR / app crashed dialogs
return canAddInternalSystemWindow ? 11 : 10;
case TYPE_APPLICATION_OVERLAY:
return 12;
case TYPE_DREAM:
// used for Dreams (screensavers with TYPE_DREAM windows)
return 13;
case TYPE_INPUT_METHOD:
// on-screen keyboards and other such input method user interfaces go here.
return 14;
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
return 15;
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_PANEL:
return 18;
case TYPE_STATUS_BAR_SUB_PANEL:
return 19;
case TYPE_KEYGUARD_DIALOG:
return 20;
case TYPE_VOLUME_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
return 21;
case TYPE_SYSTEM_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
return canAddInternalSystemWindow ? 22 : 11;
case TYPE_NAVIGATION_BAR:
// the navigation bar, if available, shows atop most things
return 23;
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
return 24;
case TYPE_SCREENSHOT:
// screenshot selection layer shouldn't go above system error, but it should cover
// navigation bars at the very least.
return 25;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
return canAddInternalSystemWindow ? 26 : 10;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
return 27;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
return 28;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
return 29;
case TYPE_ACCESSIBILITY_OVERLAY:
// overlay put by accessibility services to intercept user interaction
return 30;
case TYPE_SECURE_SYSTEM_OVERLAY:
return 31;
case TYPE_BOOT_PROGRESS:
return 32;
case TYPE_POINTER:
// the (mouse) pointer layer
return 33;
default:
Slog.e("WindowManager", "Unknown window type: " + type);
return APPLICATION_LAYER;
}
}
3.2 、mSubLayer 子序的確認
SubLayer(稱爲子序),SubLayer值是用來描述一個窗口是否屬於另外一個窗口的子窗口,或者說SubLayer值是用來確定子窗口和父窗口之間的相對位置的。
一個Activity中有三個子窗口WindowState1、WindowState2、WindowState3,WindowState1WindowState2在窗口A的前面,WindowState3在A的後面,這幾個兄弟窗口爲什麼可以這樣排序呢,這就是mSubLayer的作用,子序越大,則相對其他兄弟窗口越靠前,反之,越靠後,如果爲負數,就處在父窗口的後面,如窗口A中的WindowState3,子序是根據窗口類型調用subWindowTypeToLayerLw確定的,subWindowTypeToLayerLw同樣是在Window的構造方法中調用的。
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;
}
3.3 、窗口Z序的調整
當WindowState創建完成,並且被添加到WMS維持的數組裏面後,就需要調用WindowLayersController的assignLayersLocked(windows),進行Z序的調整。
//參數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序的計算公式計算出來的Z序值保存在WindowState的變量mBaseLayer
中,這個循環的意思是,遇到同類型的窗口,後一個窗口在前一個窗口的基礎上偏移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,也就是動畫顯示時,該窗口的層級
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;
}
// 將當前應用窗口的最高顯示層級記錄在mHighestApplicationLayer中
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
w.mWinAnimator.mAnimLayer);
}
// 對於分屏等相關的窗口,它們的顯示層級需要再次處理
collectSpecialWindows(w);
if (layerChanged) {
w.scheduleAnimationIfDimming();
}
}
// 調整特殊窗口的層級
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);
}
4.token
Android AMS(六) Activity與WMS的連接過程之AppWindowToken
WmS詳解(一)之token到底是什麼?基於Android7.0源碼
參考:
Android解析WindowManagerService(一)WMS的誕生
Android窗口系統第一篇---Window的類型與Z-Order確定