从一个Dialog的创建看Android的窗口机制(上篇)讲到一个Dialog是如何将自己的布局文件加载到DecorView的,这篇主要讲DecorView如何添加到Window上去,Dialog构造方法中创建好DecorView之后调用mWindowManager.addView(mDecor, l),我们上一篇文章分析了此处的mWindowManager就是Activity的mWindowManager
WindowManagerGlobal.addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//view是Dialog的DecorView,params是
//直接new的一个WindowManager.LayoutParams
//display是显示到哪个屏幕,parentWindow是Activity的PhoneWindow
//省略一些判断条件
.....
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//调整窗口的Token
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
......
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
int index = findViewLocked(view, false);
//大于0代表已经添加了此窗口
if (index >= 0) {
//mDyingViews里面的View代表正在删除
if (mDyingViews.contains(view)) {
//立马销毁
mRoots.get(index).doDie();
} else {
//否则抛出异常,同样的窗口不能重复添加
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
//如果是子窗口
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
//遍历所有窗口,找到其父窗口
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
//设置窗口参数
view.setLayoutParams(wparams);
//下面三个容器中的ViewRootImpl,View,LayoutParams一一对应的
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//添加窗口的核心方法
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
adjustLayoutParamsForSubWindow
这个方法在Android窗口与Token对应策略已经很详细分析过了,主要就是根据窗口的类型,子窗口,应用窗口,系统窗口获取不同的token对象,Dialog的窗口类型从从一个Dialog的创建看Android的窗口机制(上篇)得知它是应用窗口类型的
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
......
//子窗口
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}
......
//系统窗口
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
// We don't set the app token to this system window because the life cycles should be
// independent. If an app creates a system window and then the app goes to the stopped
// state, the system window should not be affected (can still show and receive input
// events)
......
} else {
//应用窗口
if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
....
}
}
我们上篇文章看到Dialog自己是没有token的,所以Dialog的token哪来的呢?这里的mContainer为空,所以Dialog的token就是当前Activity的mAppToken
我们如果使用Application或者service类型的context是无法创建Dialog的,原因是这两种context没有token,Dialog为什么需要token,就是因为创建的Dialog必须隶属于某一个Activity,你不能在别人的应用中弹出Dialog,试想一下如果我们能在后台service中创建Dialog,当用户正在使用其他应用你的service突然弹出一个Dialog,这会让用户无法区分此Dialog是哪个应用弹的,这是很危险的
好了token有了接着后面代码看
root.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......
//绘制流程,measure,layout,draw
requestLayout();
......
//将Window添加到WMS
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
}
绘制流程不去看,主要关注窗口的添加
addToDisplay
mWindowSession在ViewRootImpl构造方法中创建,用来和WMS通信,同时ViewRootImpl构造方法中还创建了一个W对象,这也是个Binder对象,通过addToDisplay传递给WMS,供WMS和应用端通信,类似与AMS和ActivityThread之间,双向通信
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
WMS.addWindow
addWindow方法中有非常多窗口合法性的判断以及添加窗口权限的判断,先看下对窗口权限的判断
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
......
......
}
PhoneWindowManager.checkAddPermission
@Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
//获取窗口的类型
int type = attrs.type;
outAppOp[0] = AppOpsManager.OP_NONE;
//如果不是应用窗口,子窗口,系统窗口的一种
//直接返回无效的类型,Andoird只有三种窗口类型
if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)
|| (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
|| (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
//如果不是系统窗口则返回OK,不需要检查权限
if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
// Window manager will make sure these are okay.
return ADD_OKAY;
}
//如果不是SystemAlert类型的窗口,下面这几种属于Alert窗口
/*
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_ERROR
TYPE_SYSTEM_OVERLAY
TYPE_APPLICATION_OVERLAY
*/
if (!isSystemAlertWindowType(type)) {
//这几种系统窗口也不需要检查权限
switch (type) {
case TYPE_TOAST:
// Only apps that target older than O SDK can add window without a token, after
// that we require a token so apps cannot add toasts directly as the token is
// added by the notification system.
// Window manager does the checking for this.
outAppOp[0] = OP_TOAST_WINDOW;
return ADD_OKAY;
case TYPE_DREAM:
case TYPE_INPUT_METHOD:
case TYPE_WALLPAPER:
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
case TYPE_VOICE_INTERACTION:
case TYPE_ACCESSIBILITY_OVERLAY:
case TYPE_QS_DIALOG:
return ADD_OKAY;
}
//非上面几种类型的窗口需要检查是否具有
//android.Manifest.permission.INTERNAL_SYSTEM_WINDOW的权限
//该权限只有系统应用才能获取
return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
== PERMISSION_GRANTED ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
// 走到这里说明添加的窗口是SystemAlert的
outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;
final int callingUid = Binder.getCallingUid();
// 如果是系统应用进程则返回OK,自动授予权限
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return ADD_OKAY;
}
ApplicationInfo appInfo;
try {
//获取当前窗口的应用信息
appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
attrs.packageName,
0 /* flags */,
UserHandle.getUserId(callingUid));
} catch (PackageManager.NameNotFoundException e) {
appInfo = null;
}
//如果当前窗口不需要应用或者Android O版本以上并且不是TYPE_APPLICATION_OVERLAY
//则也需要检查android.Manifest.permission.INTERNAL_SYSTEM_WINDOW权限
if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {
return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
//添加的SystemAlert窗口是否被应用允许
final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
//代表允许
return ADD_OKAY;
case AppOpsManager.MODE_ERRORED:
// 小于Android M的版本则允许否则拒绝
if (appInfo.targetSdkVersion < M) {
return ADD_OKAY;
}
return ADD_PERMISSION_DENIED;
default:
//检查是否具有android.Manifest.permission.SYSTEM_ALERT_WINDOW权限
return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
}
权限检查完了接着回到WMS.addWindow
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState parentWindow = null;
long origId;
final int callingUid = Binder.getCallingUid();
//窗口类型
final int type = attrs.type;
synchronized(mWindowMap) {
//Display没准备好
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
//Display不存在
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//Display不能访问
if (!displayContent.hasAccess(session.mUid)
&& !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//添加的窗口已经存在
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//找到其父窗口
parentWindow = windowForClientLocked(null, attrs.token, false);
//如果父窗口为空则添加失败
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//如果父窗口也是子窗口则添加失败
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
//如果是虚拟Private显示器窗口并且Display不是Private的则添加失败
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
AppWindowToken atoken = null;
//是否有父窗口,我们添加的Dialog不是子窗口没有父窗口
final boolean hasParent = parentWindow != null;
//获取token,如果有父窗口则使用父窗口的token
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
//如果有父窗口则使用父窗口的类型
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) {
//如果token为空并且是应用类型窗口则添加失败
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是输入法类型窗口则添加失败
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是语音交互类型窗口则添加失败
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是壁纸类型窗口则添加失败
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是屏保类型窗口则添加失败
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是TYPE_QS_DIALOG类型窗口则添加失败
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是辅助功能覆盖类型窗口则添加失败
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果token为空并且是TOAST类型窗口
if (type == TYPE_TOAST) {
//如果版本大于N MR1,则不能随意添加TOAST了
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
//如果token为空则等于窗口的W对象
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
//根据token或者W创建WindowToken
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
//token不为空并且是应用类型窗口
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//获取应用的AppWindowToken,AppWindowToken是
//WindowToken的子类标识一个Activity窗口
atoken = token.asAppWindowToken();
//如果AppWindowToken为空则添加失败
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
//如果是输入法类型窗口
} else if (rootType == TYPE_INPUT_METHOD) {
//但是token类型不是输入法则添加失败
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是语音交互类型窗口
} else if (rootType == TYPE_VOICE_INTERACTION) {
//但是token类型不是语音交互类型则添加失败
if (token.windowType != TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是壁纸类型窗口
} else if (rootType == TYPE_WALLPAPER) {
//但是token类型不是壁纸则添加失败
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是屏保类型窗口
} else if (rootType == TYPE_DREAM) {
//但是token类型不是屏保则添加失败
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是辅助功能覆盖类型窗口
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
//但是token类型不是辅助功能覆盖类型则添加失败
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是TOAST类型窗口
} else if (type == TYPE_TOAST) {
//如果是Android N MR1版本以上则不能随意添加,
//需要检查token
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
//并且token不为TOAST类型则添加失败
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是Qs Dialog类型窗口
} else if (type == TYPE_QS_DIALOG) {
//但是token类型不是Qs Dialog则添加失败
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果AppWindowToken不为空
} else if (token.asAppWindowToken() != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
//系统类型窗口但是AppWindowToken不为空
//将token置为空
attrs.token = null;
//重新创建一个新的token
token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
//创建WindowState对象,每一个成功添加的窗口都会
//在WMS对应一个WindowState,WindowState封装了窗口的信息
//WindowState里面有个很重要的步骤是调整窗口的Z轴排列顺序
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//会根据一些特殊窗口设置Flag,如截屏窗口设置不能获取事件,
//状态栏窗口在锁屏被隐藏时需要移除壁纸和键盘保护程序等
mPolicy.adjustWindowParamsLw(win.mAttrs);
//设置是否可以显示在当前uid之外的屏幕
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
//检查特殊窗口如StatusBar,NavigationBar的
//android.Manifest.permission.STATUS_BAR_SERVICE权限
res = mPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
//创建InputChannel,InputChannel用来给WMS和InputDispatcher通信
//InputChannel创建同时创建了一对socket,InputDispatch监听
//一个socket一端来获取事件处理结果,应用程序监听一个socket一端
//来接收InputDispatch发送的事件
win.openInputChannel(outInputChannel);
}
//对TOAST继续进行检查
if (type == TYPE_TOAST) {
if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {
Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
|| mCurrentFocus == null
|| mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
//到这里说明窗口是合法的可以添加
res = WindowManagerGlobal.ADD_OKAY;
if (mCurrentFocus == null) {
mWinAddedSinceNullFocus.add(win);
}
if (excludeWindowTypeFromTapOutTask(type)) {
displayContent.mTapExcludedWindows.add(win);
}
origId = Binder.clearCallingIdentity();
//此方法里会创建SurfaceComposerClient建立应用层
//和SurfaceFlinger的连接
win.attach();
//将窗口存入mWindowMap
mWindowMap.put(client.asBinder(), win);
if (win.mAppOp != AppOpsManager.OP_NONE) {
int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
(startOpResult != AppOpsManager.MODE_DEFAULT)) {
win.setAppOpVisibilityLw(false);
}
}
final AppWindowToken aToken = token.asAppWindowToken();
//如果窗口类型是启动窗口
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
//将窗口赋值给token的startwindow
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
boolean imMayMove = true;
//将窗口添加到WindowContainer的WindowList中
win.mToken.addWindow(win);
//后面还会处理一些特殊窗口
......
return res;
}
可以看出WMS的addWindow方法逻辑还是很复杂的,主要做了下面这些事:
1.根据窗口的类型检查是否具有相应权限,对非系统窗口和SystemAlert窗口不做检查,其他窗口要检查android.Manifest.permission.SYSTEM_ALERT_WINDOWh和android.Manifest.permission.INTERNAL_SYSTEM_WINDOW权限
2.对窗口的token和type合法性进行检查,比如type是输入法类型,token也必须是输入法类型,要求完全一致,比如当前窗口的父窗口也是子窗口是不允许的等
3.为每一个窗口创建WindowState,在WindowState构造方法中计算窗口的Z轴值
4.给一些特殊窗口设置flag,如截屏不需要获取焦点,StatusBar在没有锁屏时需要移除壁纸和键盘保护等
5.创建InputChannel建立InputDispatcher和应用程序的连接,使得应用程序能够接受InputDispatcher发送的事件以及InputDispatch能够得知应用程序事件处理结果
6.调用win.attach方法建立应用窗口和SurfaceFlinger的连接,使得窗口添加之后能通过SurfaceFlinger合成最终显示到屏幕上
7.根据Z轴确定最终的Window列表
从一个Dialog的创建看Android的窗口机制(上篇)和这篇文章大致梳理了一个窗口的创建与添加,系统所有的窗口流程都类似,希望这两篇文章能够达到窥一斑而知全豹的作用