前面说到,应用程序添加窗口时,会在本地创建一个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;
}
}