WindowManagerService窗口管理 1 -- addWindow源码介绍

967 阅读9分钟

上篇文章讨论了应用程序与WindowManagerService相互通信的建立过程。 应用程序向系统中添加窗口,就会调用到WindowManagerService的addWindow方法,本章来看看该方法的内部实现。添加窗口在应用进程中的代码实现以后再介绍,本节只看WMS内部实现。

窗口相关类介绍

在Android系统中窗口可分为3大类:应用窗口,子窗口和系统窗口。其中应用窗口即Activity的显示窗口。子窗口是依附于应用窗口或者系统窗口的窗口类型,它们一般随着所依附的窗口一起出现或者切换到后台。每种类型的窗口有很多种类型。 其中应用窗口的类型定义:

/**
 * Start of window types that represent normal application windows.
 */
public static final int FIRST_APPLICATION_WINDOW = 1;

/**
 * Window type: an application window that serves as the "base" window
 * of the overall application; all other application windows will
 * appear on top of it.
 * In multiuser systems shows only on the owning user's window.
 */
public static final int TYPE_BASE_APPLICATION   = 1;

/**
 * Window type: a normal application window.  The {@link #token} must be
 * an Activity token identifying who the window belongs to.
 * In multiuser systems shows only on the owning user's window.
 */
 //Activity的顶层窗口
public static final int TYPE_APPLICATION        = 2;

/**
 * Window type: special application window that is displayed while the
 * application is starting.  Not for use by applications themselves;
 * this is used by the system to display something until the
 * application can show its own windows.
 * In multiuser systems shows on all users' windows.
 */
 //应用启动时系统创建的窗口,此时应用窗口还未显示出来
public static final int TYPE_APPLICATION_STARTING = 3;

/**
 * Window type: a variation on TYPE_APPLICATION that ensures the window
 * manager will wait for this window to be drawn before the app is shown.
 * In multiuser systems shows only on the owning user's window.
 */
public static final int TYPE_DRAWN_APPLICATION = 4;

/**
 * End of types of application windows.
 */
public static final int LAST_APPLICATION_WINDOW = 99;

子窗口的类型定义:

/**
 * Start of types of sub-windows.  The {@link #token} of these windows
 * must be set to the window they are attached to.  These types of
 * windows are kept next to their attached window in Z-order, and their
 * coordinate space is relative to their attached window.
 */
public static final int FIRST_SUB_WINDOW = 1000;

/**
 * Window type: a panel on top of an application window.  These windows
 * appear on top of their attached window.
 */
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

/**
 * Window type: window for showing media (such as video).  These windows
 * are displayed behind their attached window.
 */
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

/**
 * Window type: a sub-panel on top of an application window.  These
 * windows are displayed on top their attached window and any
 * {@link #TYPE_APPLICATION_PANEL} panels.
 */
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

/** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
 * of the window happens as that of a top-level window, <em>not</em>
 * as a child of its container.
 */
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

/**
 * Window type: window for showing overlays on top of media windows.
 * These windows are displayed between TYPE_APPLICATION_MEDIA and the
 * application window.  They should be translucent to be useful.  This
 * is a big ugly hack so:
 * @hide
 */
@UnsupportedAppUsage
public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

/**
 * Window type: a above sub-panel on top of an application window and it's
 * sub-panel windows. These windows are displayed on top of their attached window
 * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels.
 * @hide
 */
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

/**
 * End of types of sub-windows.
 */
public static final int LAST_SUB_WINDOW = 1999;

系统窗口的类型定义非常多就不列举了。可以查看源码frameworks/base/core/java/android/view/WindowManager.java中的内部类LayoutParams中的定义。

WindowState 在WMS中代表一个窗口,保存了窗口相关的信息,向系统添加窗口时,在WMS的addWindow方法会创建此类的对象,它控制窗口的显示和行为。

WindowToken 一组相关窗口的容器,一个窗口的子窗口通常具有相同的token。其中变量windowType,代表窗口的类型。

AppWindowToken 继承了WindowToken,专门用于应用程序activity的显示窗口,主要是Activity创建的顶层窗口。其中的很多变量跟应用程序Activity管理相关。每个应用程序都有一个唯一的AppWindowToken

下面看看WMS的addWindow方法的代码

1. 参数检查

public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, int displayId, int requestUserId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale) {
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
        & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp);
// 检查应用是否有权限
if (res != ADD_OKAY) {
    return res;
}
    
WindowState parentWindow = null;
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
//获取要添加窗口的类型
final int type = attrs.type;
    
synchronized (mGlobalLock) {
    if (!mDisplayReady) {
        throw new IllegalStateException("Display has not been initialialized");
    }
    
    final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
    
    if (displayContent == null) {
        ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does " + "not exist: %d. Aborting.", displayId);
        return WindowManagerGlobal.ADD_INVALID_DISPLAY;
    }
    //检查是否有权限访问该display
    if (!displayContent.hasAccess(session.mUid)) {
        ProtoLog.w(WM_ERROR, "Attempted to add window to a display for which the application " + "does not have access: %d.  Aborting.", displayContent.getDisplayId());
        return WindowManagerGlobal.ADD_INVALID_DISPLAY;
    }
    
    //是否添加过窗口,不能重复添加
    if (mWindowMap.containsKey(client.asBinder())) {
        ProtoLog.w(WM_ERROR, "Window %s is already added", client);
        return WindowManagerGlobal.ADD_DUPLICATE_ADD;
    }
    //如果要添加的类型是子窗口
    if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
        parentWindow = windowForClientLocked(null, attrs.token, false);
        //添加的是子窗口,则父窗口必须存在。
        if (parentWindow == null) {
            ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
        }
        //父窗口的type不能是子窗口类型,即不能添加子窗口的子窗口
        if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
            ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
        }
    }
    
    //TYPE_PRIVATE_PRESENTATION 表示私有的演示窗口,该窗口类型是专门为演示场景设计的,可以优化屏幕显示效果,提高演示效果和用户体验
    //如果窗口类型是私有,显示设备必须是私有类型
    if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
        ProtoLog.w(WM_ERROR, "Attempted to add private presentation window to a non-private display.  " + "Aborting.");
        return WindowManagerGlobal.ADD_PERMISSION_DENIED;
    }
    
    if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {
        ProtoLog.w(WM_ERROR, "Attempted to add presentation window to a non-suitable display.  " + "Aborting.");
        return WindowManagerGlobal.ADD_INVALID_DISPLAY;
    }
    
    int userId = UserHandle.getUserId(session.mUid);
    if (requestUserId != userId) {
        try {
            mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId, false /*allowAll*/, ALLOW_NON_FULL, null, null);
        } catch (Exception exp) {
            ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",
                    requestUserId);
            return WindowManagerGlobal.ADD_INVALID_USER;
        }
        // It's fine to use this userId
        userId = requestUserId;
    }
    
    ActivityRecord activity = 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.
    //获取父窗口的token
    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.
    final int rootType = hasParent ? parentWindow.mAttrs.type : type;
    
    boolean addToastWindowRequiresToken = false;
    
    final IBinder windowContextToken = attrs.mWindowContextToken;
    //没有获取到token
    if (token == null) {
        if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, rootType, attrs.token, attrs.packageName)) {
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
        //没有获取到token但是有父窗口,子窗口使用已存在的父窗口token
        //没有父窗口则创建WindowToken
        if (hasParent) {
            // Use existing parent window token for child windows.
            token = parentWindow.mToken;
        } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
            // Respect the window context token if the user provided it.
            final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
            final Bundle options = mWindowContextListenerController
                    .getOptions(windowContextToken);
            token = new WindowToken.Builder(this, binder, type)
                    .setDisplayContent(displayContent)
                    .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                    .setRoundedCornerOverlay(isRoundedCornerOverlay)
                    .setFromClientToken(true)
                    .setOptions(options)
                    .build();
        } else {
            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            token = new WindowToken.Builder(this, binder, type)
                    .setDisplayContent(displayContent)
                    .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                    .setRoundedCornerOverlay(isRoundedCornerOverlay)
                    .build();
        }
    } else if (rootType >= FIRST_APPLICATION_WINDOW
            && rootType <= LAST_APPLICATION_WINDOW) {
        //获取到token,添加的窗口类型是应用窗口
        activity = token.asActivityRecord();
        if (activity == null) { //必须要有app token,否则退出
            ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token " + ".%s Aborting.", token);
            return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
        } else if (activity.getParent() == null) {
            ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token " + ".%s Aborting.", token);
            return WindowManagerGlobal.ADD_APP_EXITING;
        } else if (type == TYPE_APPLICATION_STARTING) {
        
            if (activity.mStartingWindow != null) {//如果是app启动窗口,并已经添加过,则退出 
                ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + "token with already existing starting window");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
            if (activity.mStartingData == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + "token but already cleaned");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
        }
    } else if (rootType == TYPE_INPUT_METHOD) { //窗口类型是输入法窗口
        if (token.windowType != TYPE_INPUT_METHOD) { //窗口类型不匹配
            ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (rootType == TYPE_VOICE_INTERACTION) {//窗口类型是语音交互层的窗口
        if (token.windowType != TYPE_VOICE_INTERACTION) { //窗口类型不匹配
            ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (rootType == TYPE_WALLPAPER) {//窗口类型是壁纸窗口
        if (token.windowType != TYPE_WALLPAPER) {//窗口类型不匹配
            ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
        if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
            ProtoLog.w(WM_ERROR, "Attempted to add Accessibility overlay window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (type == TYPE_TOAST) {//toast窗口
        // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
        addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName, callingUid, parentWindow);
        if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
            ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (type == TYPE_QS_DIALOG) {
        if (token.windowType != TYPE_QS_DIALOG) {//窗口类型不匹配
            ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token " + "%s.  Aborting.", attrs.token);
            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
    } else if (token.asActivityRecord() != null) {// 如果是系统窗口但是存在app token,则使用新token替换。
        ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d", 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.Builder(this, client.asBinder(), type)
                .setDisplayContent(displayContent)
                .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                .build();
    }

以上代码主要是根据传入的参数进行参数检查,确保传入的参数类型一致,主要检查的是有应用窗口,toast,壁纸窗口,输入法窗口,子窗口等。相关窗口的WindowToken在调用addWindow方法前已经创建好,所以这里需要检查一致性。其他合法窗口则需要创建新的WindowToken 参数检查完成后则开始创建窗口对象

2. 创建窗口对象

首先创建窗口对象WindowState,接下来检查窗口类型,调整flag参数,确保指定窗口不能获取焦点,检查窗口权限是否能被添加到系统中等。然后将创建的窗口对象保存到WMS的mWindowMap中。

//创建窗口对象WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId, session.mCanAddInternalSystemWindow);
    if (win.mDeathRecipient == null) {
        // Client has apparently died, so there is no reason to
        // continue.
        ProtoLog.w(WM_ERROR, "Adding window client %s"
                + " that is dead, aborting.", client.asBinder());
        return WindowManagerGlobal.ADD_APP_EXITING;
    }

    if (win.getDisplayContent() == null) {
        ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
        return WindowManagerGlobal.ADD_INVALID_DISPLAY;
    }
    // DisplayPolicy用于提供UI显示的基本行为和状态
    final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
    
    // 调整layout参数,确保某些类型的窗口不能获取焦点,如类型为TYPE_SYSTEM_OVERLAY,TYPE_SECURE_SYSTEM_OVERLAY,TYPE_TOAST的窗口
    displayPolicy.adjustWindowParamsLw(win, win.mAttrs);

    //FLAG_SLIPPERY标记需要检查是否有ALLOW_SLIPPERY_TOUCHES权限,没有则去除FLAG_SLIPPERY标记
    attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
    
    //设置INPUT_FEATURE_SPY标记需要MONITOR_INPUT权限,没有则抛异常
    attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid, callingPid);
    
    win.setRequestedVisibilities(requestedVisibilities);
    //根据窗口类型检查窗口是否能添加到系统中
    res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
    
    if (res != ADD_OKAY) {
        return res;
    }

    final boolean openInputChannels = (outInputChannel != null
            && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    if  (openInputChannels) {
        win.openInputChannel(outInputChannel);
    }
    
    if (type == TYPE_TOAST) {
        if (!displayContent.canAddToastWindowForUid(callingUid)) {
            ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }
        if (addToastWindowRequiresToken
                || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
                || displayContent.mCurrentFocus == null
                || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
            mH.sendMessageDelayed(
                    mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                    win.mAttrs.hideTimeoutMilliseconds);
        }
    }

    // Switch to listen to the {@link WindowToken token}'s configuration changes when
    // adding a window to the window context. Filter sub window type here because the sub
    // window must be attached to the parent window, which is attached to the window context
    // created window token.
    if (!win.isChildWindow() && mWindowContextListenerController.hasListener(windowContextToken)) {
        final int windowContextType = mWindowContextListenerController.getWindowType(windowContextToken);
        final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);
        if (type != windowContextType) {
            ProtoLog.w(WM_ERROR, "Window types in WindowContext and" + " LayoutParams.type should match! Type from LayoutParams is %d,"+ " but type from WindowContext is %d", type, windowContextType);
            // We allow WindowProviderService to add window other than windowContextType,
            // but the WindowProviderService won't be associated with the window's
            // WindowToken.
            if (!isWindowProviderService(options)) {
                return WindowManagerGlobal.ADD_INVALID_TYPE;
            }
        } else {
            mWindowContextListenerController.registerWindowContainerListener(
                    windowContextToken, token, callingUid, type, options);
        }
    }

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

    res = ADD_OKAY;

    if (mUseBLAST) {
        res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
    }

    if (displayContent.mCurrentFocus == null) {
        displayContent.mWinAddedSinceNullFocus.add(win);
    }

    if (excludeWindowTypeFromTapOutTask(type)) {
        displayContent.mTapExcludedWindows.add(win);
    }
    
    win.attach();
    // 将当前创建的窗口WindowState对象win,添加到保存窗口对象的map中。
    mWindowMap.put(client.asBinder(), win);
    win.initAppOpsState();
    
    ...

3. 更新配置信息,子视图分配层级


    ...
    
    final WindowStateAnimator winAnimator = win.mWinAnimator;
    winAnimator.mEnterAnimationPending = true;
    winAnimator.mEnteringAnimation = true;
    // Check if we need to prepare a transition for replacing window first.
    if (!win.mTransitionController.isShellTransitionsEnabled()
            && activity != null && activity.isVisible()
            && !prepareWindowReplacementTransition(activity)) {
        // 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(activity);
    }

    if (displayPolicy.areSystemBarsForcedConsumedLw()) {
        res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
    }

    if (mInTouchMode) {
        res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;//加入支持触屏标志 
    }
    if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
        res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;//加入应用可见标志 
    }
    //设置更新输入法窗口标志
    displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();

    boolean focusChanged = false;
    if (win.canReceiveKeys()) {
        //如果窗口能接受输入,计算是否会引起焦点变化 。
        //调用updateFocusedWindowLocked方法重新确定系统的焦点位置
        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*/);
    }
    
    //更新输入法窗口的信息 
    displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);

    ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
            + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));

    boolean needToSendNewConfiguration =
            win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
    if (win.providesNonDecorInsets()) {
        needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
    }
    if (needToSendNewConfiguration) {
        displayContent.sendNewConfiguration();  //更新系统的配置
    }

    // This window doesn't have a frame yet. Don't let this window cause the insets change.
    displayContent.getInsetsStateController().updateAboveInsetsState(
            false /* notifyInsetsChanged */);

    outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
    getInsetsSourceControls(win, outActiveControls);

    if (win.mLayoutAttached) {
        outAttachedFrame.set(win.getParentWindow().getFrame());
        if (win.mInvGlobalScale != 1f) {
            outAttachedFrame.scale(win.mInvGlobalScale);
        }
    } else {
        // Make this invalid which indicates a null attached frame.
        outAttachedFrame.set(0, 0, -1, -1);
    }
    outSizeCompatScale[0] = win.getCompatScaleForClient();
}

Binder.restoreCallingIdentity(origId);

return res;

addWindow方法中首先根据窗口type类型进行参数的检查,确保窗口能正确添加,接下来创建窗口对象WindowState,该对象保存了窗口相关信息。WindowState代表一个窗口,WMS中管理所有窗口,其中一个map对象mWindowMap中会保存所有窗口的对象,当WindowState对象创建完成后,会被加入到mWindowMap中。然后更新输入法标记,系统配置等信息,为当前窗口的子视图分配层级等。可以看到在这个方法中对输入法窗口,toast,壁纸等窗口这些特殊窗口做了一些处理。 Android系统中的窗口都按照次序排列在Z轴上,新建的窗口需要确定好插入到Z轴中的位置。每个窗口根据不同的类型和情况会计算一个值,它的大小即是Z轴中出现的位置。

下一章我们再仔细介绍一下窗口Z轴位置的确定

源码地址:

DisplayContent: frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java WindowToken:frameworks/base/services/core/java/com/android/server/wm/WindowToken.java WindowState:frameworks/base/services/core/java/com/android/server/wm/WindowState.java WindowManagerService:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java WindowManager.LayoutParams:frameworks/base/core/java/android/view/WindowManager.java