Android入门之addWindow

        前面说到,应用程序添加窗口时,会在本地创建一个ViewRoot,然后通过IPC(进程间通信)调用WmS的Session的addWindow请求WmS创建窗口,下面来看看addWindow方法。

        addWindow方法定义在frameworks/base/services/java/com.android.server.WindowManagerService.java中,其代码如下所示:

public int addWindow(Session session, IWindow client,
		WindowManager.LayoutParams attrs, int viewVisibility,
		Rect outContentInsets, InputChannel outInputChannel) {
	// 是否有添加权限
	int res = mPolicy.checkAddPermission(attrs);
	if (res != WindowManagerImpl.ADD_OKAY) {
		return res;
	}

	boolean reportNewConfig = false;
	WindowState attachedWindow = null;
	WindowState win = null;

	synchronized(mWindowMap) {
		// Instantiating a Display requires talking with the simulator,
		// so don't do it until we know the system is mostly up and
		// running.
		// 是否存在显示设置
		if (mDisplay == null) {
			// 若不存在,则获取系统设置
			WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
			mDisplay = wm.getDefaultDisplay();
			mInitialDisplayWidth = mDisplay.getWidth();
			mInitialDisplayHeight = mDisplay.getHeight();
			// 将Display存放到InputManager中
			mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
			reportNewConfig = true;
		}

		// 是否重复添加
		if (mWindowMap.containsKey(client.asBinder())) {
			Slog.w(TAG, "Window " + client + " is already added");
			return WindowManagerImpl.ADD_DUPLICATE_ADD;
		}

		// 是否子窗口
		if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
			// 若为子窗口
			// 返回WmS中存在的对应父窗口,若不存在则返回null
			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 WindowManagerImpl.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 WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
			}
		}

		boolean addToken = false;
		// 在WmS中寻找对应的WindowToken
		WindowToken token = mTokenMap.get(attrs.token);
		if (token == null) {
			if (attrs.type >= FIRST_APPLICATION_WINDOW
					&& attrs.type <= LAST_APPLICATION_WINDOW) {
				// 对于子窗口来说,WmS中必须有对应的Token才能添加
				Slog.w(TAG, "Attempted to add application window with unknown token "
					  + attrs.token + ".  Aborting.");
				return WindowManagerImpl.ADD_BAD_APP_TOKEN;
			}
			if (attrs.type == TYPE_INPUT_METHOD) {
				// 如果是内置的输入方法窗口,WmS中必须有对应的Token才能添加
				Slog.w(TAG, "Attempted to add input method window with unknown token "
					  + attrs.token + ".  Aborting.");
				return WindowManagerImpl.ADD_BAD_APP_TOKEN;
			}
			if (attrs.type == TYPE_WALLPAPER) {
				// 墙纸窗口,WmS中必须有对应的Token才能添加
				Slog.w(TAG, "Attempted to add wallpaper window with unknown token "
					  + attrs.token + ".  Aborting.");
				return WindowManagerImpl.ADD_BAD_APP_TOKEN;
			}
			// 创建窗口
			token = new WindowToken(attrs.token, -1, false);
			addToken = true;
		} else if (attrs.type >= FIRST_APPLICATION_WINDOW
				&& attrs.type <= LAST_APPLICATION_WINDOW) {
			// token不为null且是应用窗口
			AppWindowToken atoken = token.appWindowToken;
			if (atoken == null) {
				// appWindowToken值不能为空
				Slog.w(TAG, "Attempted to add window with non-application token "
					  + token + ".  Aborting.");
				return WindowManagerImpl.ADD_NOT_APP_TOKEN;
			} else if (atoken.removed) {
				// 试图使用存在的应用token添加窗口
				Slog.w(TAG, "Attempted to add window with exiting application token "
					  + token + ".  Aborting.");
				return WindowManagerImpl.ADD_APP_EXITING;
			}
			if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
				// No need for this guy!
				// 窗口类型不能是应用启动时显示的窗口
				if (localLOGV) Slog.v(
						TAG, "**** NO NEED TO START: " + attrs.getTitle());
				return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;
			}
		} else if (attrs.type == TYPE_INPUT_METHOD) {
			// 对于内置的输入方法窗口,token的windowType值要等于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 WindowManagerImpl.ADD_BAD_APP_TOKEN;
			}
		} else if (attrs.type == TYPE_WALLPAPER) {
			// 对于墙纸窗口,token的windowType值要等于TYPE_WALLPAPER
			if (token.windowType != TYPE_WALLPAPER) {
				Slog.w(TAG, "Attempted to add wallpaper window with bad token "
						+ attrs.token + ".  Aborting.");
				  return WindowManagerImpl.ADD_BAD_APP_TOKEN;
			}
		}

		// 创建窗口
		win = new WindowState(session, client, token,
				attachedWindow, attrs, viewVisibility);
		if (win.mDeathRecipient == null) {
			// Client has apparently died, so there is no reason to
			// continue.
			// 客户端已被销毁,所以没必要继续
			Slog.w(TAG, "Adding window client " + client.asBinder()
					+ " that is dead, aborting.");
			return WindowManagerImpl.ADD_APP_EXITING;
		}

		// 如果是Toast,则此窗口不能够接收input事件
		mPolicy.adjustWindowParamsLw(win.mAttrs);

		// 判断添加的窗口是单例还是多例
		res = mPolicy.prepareAddWindowLw(win, attrs);
		if (res != WindowManagerImpl.ADD_OKAY) {
			// 是多例则直接返回
			return res;
		}
		
		// 如果输出的Channel,也即Pipe中的读通道为空
		if (outInputChannel != null) {
			// 创建通道
			String name = win.makeInputChannelName();
			InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
			win.mInputChannel = inputChannels[0];
			inputChannels[1].transferToBinderOutParameter(outInputChannel);
			// 在InputManager中注册通道
			mInputManager.registerInputChannel(win.mInputChannel);
		}

		// From now on, no exceptions or errors allowed!

		res = WindowManagerImpl.ADD_OKAY;

		// 重置当前线程的IPC的ID
		final long origId = Binder.clearCallingIdentity();

		// 从上述代码中得出是否要添加Token,若是则添加Token添加到WmS中
		if (addToken) {
			mTokenMap.put(attrs.token, token);
			mTokenList.add(token);
		}
		// 将窗口添加到Session中
		win.attach();
		// 窗口信息添加到WmS中
		mWindowMap.put(client.asBinder(), win);

		if (attrs.type == TYPE_APPLICATION_STARTING &&
				token.appWindowToken != null) {
			// 对于应用启动时显示的窗口,设置token
			token.appWindowToken.startingWindow = win;
		}

		boolean imMayMove = true;

		if (attrs.type == TYPE_INPUT_METHOD) {
			// 内置的输入方法窗口
			mInputMethodWindow = win;
			addInputMethodWindowToListLocked(win);
			imMayMove = false;
		} else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
			// 内置的输入方法对话框窗口
			mInputMethodDialogs.add(win);
			addWindowToListInOrderLocked(win, true);
			adjustInputMethodDialogsLocked();
			imMayMove = false;
		} else {
			// 其他窗口
			addWindowToListInOrderLocked(win, true);
			if (attrs.type == TYPE_WALLPAPER) {
				mLastWallpaperTimeoutTime = 0;
				adjustWallpaperWindowsLocked();
			} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
				adjustWallpaperWindowsLocked();
			}
		}

		win.mEnterAnimationPending = true;

		// 获取系统窗口区域的insets
		mPolicy.getContentInsetHintLw(attrs, outContentInsets);

		if (mInTouchMode) {
			// 用户直接触摸的窗口
			res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
		}
		if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {
			// 应用窗口
			res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
		}

		boolean focusChanged = false;
		if (win.canReceiveKeys()) {
			// 窗口需要按键事件
			// 更新焦点,将窗口信息写入了InputDispatcher
			focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
			if (focusChanged) {
				imMayMove = false;
			}
		}

		if (imMayMove) {
			// 若需要锁定的话,移动输入方法窗口
			moveInputMethodWindowsIfNeededLocked(false);
		}

		assignLayersLocked();
		// Don't do layout here, the window must call
		// relayout to be displayed, so we'll do it there.

		//dump();

		if (focusChanged) {
			finishUpdateFocusedWindowAfterAssignLayersLocked();
		}
		
		if (localLOGV) Slog.v(
			TAG, "New client " + client.asBinder()
			+ ": window=" + win);
		
		if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked()) {
			reportNewConfig = true;
		}
	}

	// sendNewConfiguration() checks caller permissions so we must call it with
	// privilege.  updateOrientationFromAppTokens() clears and resets the caller
	// identity anyway, so it's safe to just clear & restore around this whole
	// block.
	final long origId = Binder.clearCallingIdentity();
	if (reportNewConfig) {
		sendNewConfiguration();
	}
	Binder.restoreCallingIdentity(origId);

	return res;
}

        有些东西还没摸明白,后面深入学习后再补一下。

        上文还说到,addWindow会将窗口信息写入InputDispatcher,其实在addWindow代码中有体现:

		if (win.canReceiveKeys()) {
			// 窗口需要按键事件
			// 更新焦点,在这里,将窗口信息写入了InputDispatcher
			focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
			if (focusChanged) {
				imMayMove = false;
			}
		}

        至于如何写入InputDispatcher,下文分析。


你可能感兴趣的:(Android,Android,WmS,addWindow)