在第一讲的时候我们说WindowManager类继承于ViewManager,ViewManager是一个接口类。实现了三个方法,对应的分别是窗口的添加、更新、删除。
我们下面对于这三个Window的操作流程来详述。
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
*
Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
WindowManager中没有addView的实现,那么我们需要继续看WindowManager的子类WindowManagerImpl中是否有实现。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
第一讲有介绍过WindowManagerImpl和WindowManagerGlobal之间使用桥接模式,WindowManagerImpl中的addView不执行具体的方法,转而在WindowManagerGlobal中实现:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 1 start
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
// 1 end
ViewRootImpl root;
View panelParentView = null;
// 2 start
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
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);
}
}
}
// 2 end
// 3 start
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 3 end
// 4 start
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
// 4 end
}
}
注释1:检查传入的参数是否合法;
注释2:检查系统属性值;
注释3:创建一个ViewRootImpl对象,并将window的一系列对象添加到列表中;
注释4:通过ViewRootImpl的setView()方法更新界面,完成window的添加过程;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
// 1
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// 2
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
......
}
}
}
setView()方法中有很多逻辑,我们截取和window添加有关的部分。
注释1:调用ViewRootImpl的requestLayout()方法,完成异步刷新请求;这个方法最终会调用到ViewRootImpl的scheduleTraversals()方法,这个方法就是View绘制的入口方法。
注释2:调用addToDisplay()方法。
IWindowSession是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,Server端的实现是Session。
之前的代码逻辑都是运行在本地进程中,而Session的addToDisplay()方法则运行在WMS所在的进程(即SystemServer进程)。
@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()方法了。
这个方法中的逻辑比较多,大概的实现逻辑我都在代码中注释了。
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
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 (mGlobalLock) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
// 获取窗口要添加到哪个DisplayContent对象上
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
// 检查displayContent对象有效性
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;
}
if (!displayContent.hasAccess(session.mUid)) {
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;
}
// 判读窗口类型是否是子窗口(type值介于1000~1999之间),是则走进入if语句中
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;
}
// 检查父窗口的type值不能介于1000~1999之间(即父窗口不能也是一个子窗口)
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;
}
}
// 窗口类型不能是TYPE_PRIVATE_PRESENTATION
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;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
// 获取windowToken对象
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
// 如果是父窗口就将父窗口的type赋值给rootType对象,否则就赋予当前窗口的type值
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
// 如果token为空,根据rootType值进行区分判断
if (token == null) {
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
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;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
final boolean isRoundedCornerOverlay =
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
// 隐式创建一个新的WindowToken对象
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
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 (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+ " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
} else if (rootType == TYPE_INPUT_METHOD) {
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) {
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) {
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) {
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) {
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;
}
} else if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
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;
}
} else if (type == TYPE_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;
}
} else if (token.asAppWindowToken() != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
// 创建WindowState对象
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;
}
// 判断窗口的DisplayContent是否为空
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
// 获取DisplayPolicy对象
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
// 调用adjustWindowParamsLw方法(会根据窗口的type对窗口的LayoutParams的一些成员变量进行修改)
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
Binder.getCallingUid());
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
// 调用prepareAddWindowLw(准备将window添加到系统中)
res = displayPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
// input相关
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
// If adding a toast requires a token for this app we always schedule hiding
// toast windows to make sure they don't stick around longer then necessary.
// We hide instead of remove such windows as apps aren't prepared to handle
// windows being removed under them.
//
// If the app is older it can add toasts without a token and hence overlay
// other apps. To be maximally compatible with these apps we will hide the
// window after the toast timeout only if the focused window is from another
// UID, otherwise we allow unlimited duration. When a UID looses focus we
// schedule hiding all of its toast windows.
// 针对窗口类型是toast的情况
if (type == TYPE_TOAST) {
if (!displayContent.canAddToastWindowForUid(callingUid)) {
Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// Make sure this happens before we moved focus as one can make the
// toast focusable to force it not being hidden after the timeout.
// Focusable toasts are always timed out to prevent a focused app to
// show a focusable toasts while it has focus which will be kept on
// the screen after the activity goes away.
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
|| displayContent.mCurrentFocus == null
|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
}
if (excludeWindowTypeFromTapOutTask(type)) {
displayContent.mTapExcludedWindows.add(win);
}
origId = Binder.clearCallingIdentity();
// window attach
win.attach();
// 将windowState对象添加到mWindowMap中
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
UserHandle.getUserId(win.getOwningUid()));
win.setHiddenWhileSuspended(suspended);
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
final AppWindowToken aToken = token.asAppWindowToken();
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
boolean imMayMove = true;
// 将windowState对象添加到windowToken中
win.mToken.addWindow(win);
// 根据type值区分判断,并赋值imMayMove为false
if (type == TYPE_INPUT_METHOD) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
if (type == TYPE_WALLPAPER) {
displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
// This is to avoid a new window being placed between the
// wallpaper and its target.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
// If the window is being added to a stack that's currently adjusted for IME,
// make sure to apply the same adjust to this new window.
// 针对IME调整窗口
win.applyAdjustForImeIfNeeded();
if (type == TYPE_DOCK_DIVIDER) {
mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
}
// 获取WindowStateAnimator对象
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
if (atoken != null && atoken.isVisible()
&& !prepareWindowReplacementTransition(atoken)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
// the app is relaunching.
prepareNoneTransitionForRelaunching(atoken);
}
final DisplayFrames displayFrames = displayContent.mDisplayFrames;
// TODO: Not sure if onDisplayInfoUpdated() call is needed.
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
// DisplayInfo状态更新
displayFrames.onDisplayInfoUpdated(displayInfo,
displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
final Rect taskBounds;
final boolean floatingStack;
if (atoken != null && atoken.getTask() != null) {
taskBounds = mTmpRect;
atoken.getTask().getBounds(mTmpRect);
floatingStack = atoken.getTask().isFloating();
} else {
taskBounds = null;
floatingStack = false;
}
if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
}
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
win.getParent().assignChildLayers();
if (focusChanged) {
displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
//更新input窗口
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
if (win.isVisibleOrAdding() && displayContent.updateOrientationFromAppTokens()) {
reportNewConfig = true;
}
}
if (reportNewConfig) {
sendNewConfiguration(displayId);
}
Binder.restoreCallingIdentity(origId);
return res;
}
总结:
当客户端调用WindowManager类的addView()方法后,该方法会创建一个新的ViewRootImpl对象,然后调用ViewRootImpl类的setView()方法,这个方法会通过IPC方式调用到WMS的addWindow()方法。
而WMS的addWindow()方法主要做了如下几件事:
1.对添加的窗口进行检查
2.WindowToken的处理,比如窗口类型的判断
3.WindowState的创建,并将WindowState放到windowToken中去;
4.创建和配置DisplayContent;
大致的流程图如下:
关于Winow的删除动作,有两种:一是异步删除(removeView);二是同步删除(removeViewImmediate)。最终都是调用WindowManagerGlobal的removeView()方法。区别就是传参的第二个参数一个是true,一个是false。
我们这里以removeView为例。
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
直接看WindowManagerGlobal中的实现
@UnsupportedAppUsage
public void removeView(View view, boolean immediate) {
// 判断参数View是否为空
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// 查找需要删除的View的索引,并赋值给index
int index = findViewLocked(view, true);
// 根据index索引找到对应的View
View curView = mRoots.get(index).getView();
// 调用removeViewLocked方法
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
// 获取videRootImpl对象
ViewRootImpl root = mRoots.get(index);
// 获取对应的View对象
View view = root.getView();
// view不为空
if (view != null) {
// 获取InputMethodManager对象
InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
// 调用ViewRootImpl的die方法
boolean deferred = root.die(immediate);
// 如果View不为空,将当前父窗口作为窗口
if (view != null) {
view.assignParent(null);
// 如果deferred为true,将该View加入到mDyingViews序列中
if (deferred) {
mDyingViews.add(view);
}
}
}
主要调用die方法
这里发送一个MSG_DIE的消息。
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
// 如果调用的是removeViewImmediate,这里就会执行,直接调用doDie方法
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
还是调用doDie()方法
case MSG_DIE:
doDie();
break;
void doDie() {
// 判断thread是否是当前进程,即线程的正确性
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
// 如果有子View
if (mAdded) {
// 真正删除View的逻辑
dispatchDetachedFromWindow();
}
// 如果有子View且不是第一次被添加
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
destroySurface();
}
}
mAdded = false;
}
// 调用doRemoveView方法
WindowManagerGlobal.getInstance().doRemoveView(this);
}
G.ViewRootImpl#dispatchDetachedFromWindow
void dispatchDetachedFromWindow() {
mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
// 1
mView.dispatchDetachedFromWindow();
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
destroySurface();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
try {
// 2
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
mDisplayManager.unregisterDisplayListener(mDisplayListener);
unscheduleTraversals();
}
注释1:调用View的dispatchDetachedFromWindow()方法;
注释2:通过Session的remove()方法来删除Window, mWindowSession是IWindowSeesion类型的,在Window的添加流程中我们知道,Binder的服务端是Session类
@Override
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
这就调用到WMS中去处理了
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
// 创建一个WindowState对象
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return;
}
// 调用WindowState的removeIfPossible方法
win.removeIfPossible();
}
}
@Override
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
mWindowRemovalAllowed = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG,
"removeIfPossible: " + this + " callers=" + Debug.getCallers(5));
final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
"Starting window removed " + this);
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
Slog.v(TAG_WM, "Remove " + this + " client="
+ Integer.toHexString(System.identityHashCode(mClient.asBinder()))
+ ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
+ Debug.getCallers(5));
final long origId = Binder.clearCallingIdentity();
try {
// 清除pipe对
disposeInputChannel();
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
+ ": mSurfaceController=" + mWinAnimator.mSurfaceController
+ " mAnimatingExit=" + mAnimatingExit
+ " mRemoveOnExit=" + mRemoveOnExit
+ " mHasSurface=" + mHasSurface
+ " surfaceShowing=" + mWinAnimator.getShown()
+ " animating=" + isAnimating()
+ " app-animation="
+ (mAppToken != null ? mAppToken.isSelfAnimating() : "false")
+ " mWillReplaceWindow=" + mWillReplaceWindow
+ " inPendingTransaction="
+ (mAppToken != null ? mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mWmService.mDisplayFrozen
+ " callers=" + Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
final int displayId = getDisplayId();
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
// 1.判断我们是否需要窗口动画
// 如果要,则等待动画结束后再删除窗口;否则,如果窗口处于冻屏,则立刻删除窗口
if (mHasSurface && mToken.okToAnimate()) {
if (mWillReplaceWindow) {
// This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Preserving " + this + " until the new one is " + "added");
// TODO: We are overloading mAnimatingExit flag to prevent the window state from
// been removed. We probably need another flag to indicate that window removal
// should be deffered vs. overloading the flag that says we are playing an exit
// animation.
mAnimatingExit = true;
mReplacingRemoveRequested = true;
return;
}
// If we are not currently running the exit animation, we need to see about starting one
wasVisible = isWinVisibleLw();
if (keepVisibleDeadWindow) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Not removing " + this + " because app died while it's visible");
mAppDied = true;
setDisplayLayoutNeeded();
mWmService.mWindowPlacerLocked.performSurfacePlacement();
// Set up a replacement input channel since the app is now dead.
// We need to catch tapping on the dead window to restart the app.
openInputChannel(null);
getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
return;
}
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
// Try starting an animation.
if (mWinAnimator.applyAnimationLocked(transit, false)) {
mAnimatingExit = true;
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
// any change from that is performed immediately.
setDisplayLayoutNeeded();
mWmService.requestTraversal();
}
if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
final boolean isAnimating = isAnimating()
&& (mAppToken == null || !mAppToken.isWaitingForTransitionStart());
final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
&& mAppToken.isLastWindow(this);
// We delay the removal of a window if it has a showing surface that can be used to run
// exit animation and it is marked as exiting.
// Also, If isn't the an animating starting window that is the last window in the app.
// We allow the removal of the non-animating starting window now as there is no
// additional window or animation that will trigger its removal.
if (mWinAnimator.getShown() && mAnimatingExit
&& (!lastWindowIsStartingWindow || isAnimating)) {
// The exit animation is running or should run... wait for it!
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Not removing " + this + " due to exit animation ");
setupWindowForRemoveOnExit();
if (mAppToken != null) {
mAppToken.updateReportedVisibilityLocked();
}
return;
}
}
// 2.删除窗口
removeImmediately();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent.updateOrientationFromAppTokens()) {
displayContent.sendNewConfiguration();
}
}
// 3.重新寻找焦点(交互)窗口
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
void removeImmediately() {
super.removeImmediately();
// 防止重复删除
if (mRemoved) {
// Nothing to do.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"WS.removeImmediately: " + this + " Already removed...");
return;
}
mRemoved = true;
mWillReplaceWindow = false;
if (mReplacementWindow != null) {
mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
}
final DisplayContent dc = getDisplayContent();
if (isInputMethodTarget()) {
dc.computeImeTarget(true /* updateImeTarget */);
}
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
dc.mTapExcludedWindows.remove(this);
}
if (mTapExcludeRegionHolder != null) {
// If a tap exclude region container was initialized for this window, then it should've
// also been registered in display.
dc.mTapExcludeProvidingWindows.remove(this);
}
// 如果window是StatusBar或者NavigationBar,删除窗口
dc.getDisplayPolicy().removeWindowLw(this);
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
// 将view对应的session从mSessions删除
mSession.windowRemovedLocked();
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
// Ignore if it has already been removed (usually because
// we are doing this as part of processing a death note.)
}
// 调用WMS的postWindowRemoveCleanupLocked对View进行清理工作
mWmService.postWindowRemoveCleanupLocked(this);
}
总结:
关于Window的删除过程大致如上述,总的删除过程可以总结为以下:
1.检查线程是否是当前线程,即线程的正确性;
2.从ViewRootImpl列表、View列表中删除与View对应的元素;
3.判断是否可以立即执行删除操作,否则就推迟删除操作;
4.执行删除操作,清理和删除和View相关的一切元素。
流程图如下:
ViewManager接口类中定义了updateViewLayout()方法,该方法是用来实现窗口的更新的。我们还是直接从WindowManagerImpl中updateViewLayout()方法开始。
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
// 参数检查
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// 创建WindowManager.LayoutParams对象
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
// 更新view的LayouParams
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
// 更新ViewRootImpl的LayoutParams
root.setLayoutParams(wparams, false);
}
}
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
......
// Don't lose the mode we last auto-computed.
if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
== WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
& ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
}
mWindowAttributesChanged = true;
scheduleTraversals();
}
}
这个方法最后会调用到scheduleTraversals()方法
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// postCallback发起回调
// 最终会调用到ViewRootImpl的performTraversals方法,开始View的工作流程
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// 关键方法
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
这个方法不列代码了,这是View绘制的核心方法,其中的逻辑顺序就是:measure、layout、draw方法的实现。因此也就更新了View的视图,对应的Window就更新了。
总结:
附上流程图