天天看點

Android系統視窗管理機制簡介

一、概述

Android系統視窗管理是由WindowManagerService負責實作的.WindowManagerService(後面簡稱WMS)的代碼位于

frameworks/base/services/java/com/android/server/wm/WindowManagerService.java.

什麼是視窗?

視窗就是螢幕上的一塊矩形區域,可以顯示UI和與使用者互動.常見的比如:Dialog,Activity界面,狀态欄、Toast界面.站在系統的角度來說,

視窗其實是一個Surface(畫布).一個螢幕有多個視窗,而這多個視窗的布局和順序以及視窗動畫是由WMS管理的,然後由一個叫SurfaceFlinger的服務來對多個畫布内容混合和顯示出來.

WMS和SurfaceFlinger的關系如下圖

Android系統視窗管理機制簡介

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

Android系統視窗管理機制簡介