Android系統窗口管理機制簡介

一、概述


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管理窗口的機制要遠比我分析的要複雜,能力有限,我只能介紹這麼多了.再見







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章