一、概述
Android系統窗口管理是由WindowManagerService負責實現的.WindowManagerService(後面簡稱WMS)的代碼位於
frameworks/base/services/java/com/android/server/wm/WindowManagerService.java.
什麼是窗口?
窗口就是屏幕上的一塊矩形區域,可以顯示UI和與用戶交互.常見的比如:Dialog,Activity界面,狀態欄、Toast界面.站在系統的角度來說,
窗口其實是一個Surface(畫布).一個屏幕有多個窗口,而這多個窗口的佈局和順序以及窗口動畫是由WMS管理的,然後由一個叫SurfaceFlinger的服務來對多個畫布內容混合和顯示出來.
WMS和SurfaceFlinger的關係如下圖
圖中的Z軸大小就是不同窗口顯示的順序,在Android裏叫Z-order.SurfaceFlinger將多塊Surface的內容按照Z-order進行混合並輸出到FrameBuffer(幀緩衝).
二、WMS的啓動
和AMS、PMS一樣,WMS也是在SystemServer的initAndLoop方法裏啓動的.
主要有3個階段:
1.創建WMS
2.做顯示準備工作
3.SystemServer啓動之後通知WMS
先看第一個階段
1.創建WMS
wm = WindowManagerService.main(context, power, display, inputManager, wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
新建WMS實例,然後往ServiceManager註冊WMS.
看下main方法
public static WindowManagerService main(final Context context, final PowerManagerService pm, final DisplayManagerService dm, final InputManagerService im, final Handler wmHandler, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
wmHandler.runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = new WindowManagerService(context, pm, dm, im, haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
新建了一個WMS類型的數組,大小爲1,然後new一個WMS實例,賦值給該數組,最後返回這個數組.
調用了WMS的有參構造函數
private WindowManagerService(Context context, PowerManagerService pm, DisplayManagerService displayManager, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
......
......
//新建窗口動畫實例
mAnimator = new WindowAnimator(this);
//在UI線程中初始化WindowManagerPolicy
initPolicy(UiThread.getHandler());
// 添加自己到Watchdog中
Watchdog.getInstance().addMonitor(this);
......
......
主要是新建窗口動畫,在UI線程中初始化WindowManagerPolicy,添加自己到Watchdog中.
這裏介紹下WindowManagerPolicy,WindowManagerPolicy是一個接口,它只有一個實現類PhoneWindowManager.
它相當於WMS的中介,會爲WMS處理窗口信息.比如計算窗口尺寸等.
2.做顯示準備工作
public void displayReady() {
//顯示默認的尺寸、像素等配置
displayReady(Display.DEFAULT_DISPLAY);
synchronized (mWindowMap) {
//獲取屏幕
final DisplayContent displayContent = getDefaultDisplayContentLocked();
//讀取屏幕尺寸和像素等信息
readForcedDisplaySizeAndDensityLocked(displayContent);
mDisplayReady = true;
}
......
......
}
會先配置默認的尺寸、像素等顯示信息.然後獲取屏幕,讀取屏幕尺寸和像素等信息.這裏要介紹下DisplayContent和mWindowMap.
DisplayContent是一塊屏幕.用列表保存的.
SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2);
DisplayContent會根據窗口的位置顯示出窗口,屬於同一個DisplayContent的窗口就顯示在同一個屏幕裏.
mWindowMap一個HashMap,保存了所有窗口的狀態信息.鍵爲IBinder,值爲WindowToken.
WindowToken是窗口令牌的意思,用來標示該窗口的類別.窗口有Activity、InputMethod、Wallpaper以及Dream這幾種.不同的類別對應不同的WindowToken.
3.SystemServer啓動之後通知WMS
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
調用的是WindowManagerPolicy的systemReady方法
public void systemReady() {
mPolicy.systemReady();
}
三、窗口的添加和刪除
WMS主要的功能如下
1. 窗口的添加和刪除
2. 窗口的顯示和隱藏控制
3. Z-order順序管理
4. 焦點窗口和焦點應用的管理
5. 輸入法窗口管理和牆紙窗口管理
6. 窗口動畫管理
7. 系統消息收集和分發
這裏暫時只介紹Activity窗口的添加.
AMS啓動一個應用時,會在服務端生成一個AppWindowToken,AppWindowToken繼承自WindowToken,標示爲Activity的WindowToken.
Activity在請求WMS添加窗口時,提供這個AppWindowToken給WMS.
添加窗口的方法是addWindow.看代碼吧.
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) {
......
......
//通過WindowManagerPolicy檢查權限,是否能添加窗口
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
//添加子窗口的父窗口的窗口狀態
WindowState attachedWindow = null;
//子窗口的窗口狀態
WindowState win = null;
long origId;
//窗口類型,比如Toast、StatusBar
final int type = attrs.type;
synchronized (mWindowMap) {
//屏幕設置未初始化
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
//如果重複添加了
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//返回該子窗口在WMS對應的父窗口
attachedWindow = windowForClientLocked(null, attrs.token, false);
//如果父窗口不存在
if (attachedWindow == null) {
Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
// 若取得的父窗口也是子窗口,則打印:添加了錯誤的子窗口
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
boolean addToken = false;
//取出WindowToken
WindowToken token = mTokenMap.get(attrs.token);
if (token == null) {
// 對於子窗口來說,WmS中必須有對應的Token才能添加
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 如果是內置的輸入方法窗口,WmS中必須有對應的Token才能添加
if (type == TYPE_INPUT_METHOD) {
Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 牆紙窗口,WmS中必須有對應的Token才能添加
if (type == TYPE_WALLPAPER) {
Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_DREAM) {
Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 創建窗口
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
}
//Activity類型窗口
else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
AppWindowToken atoken = token.appWindowToken;
// appWindowToken值爲空則打印信息
if (atoken == null) {
Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
}
// 試圖使用存在的應用token添加窗口
else if (atoken.removed) {
Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
// 對於內置的輸入方法窗口,token的windowType值要等於TYPE_INPUT_METHOD
if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
// No need for this guy!
if (localLOGV)
Slog.v(TAG, "**** NO NEED TO START: " + attrs.getTitle());
return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
}
}
//輸入法類型窗口
else if (type == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
//壁紙類型窗口
else if (type == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
//DREAM類型窗口
else if (type == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
//創建窗口
win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
//調整子窗口尺寸
mPolicy.adjustWindowParamsLw(win.mAttrs);
......
......
// 如果要添加Token,添加Token添加到WmS中
if (addToken) {
mTokenMap.put(attrs.token, token);
}
// 將窗口添加到Session中
win.attach();
......
......
}
Session表示一個客戶端和服務端的交互會話。一般來說不同的應用通過不同的會話來和WindowManagerService交互,但是處於同一個進程的不同應用通過同一個Session來交互。
結束語:關於WMS管理窗口的機制要遠比我分析的要複雜,能力有限,我只能介紹這麼多了.