Android WMS窗口管理

       WindowManagerService跟ActivityManagerService一样都是系统核心服务,通过SystemServer来启动的,ActivityManagerService负责Activity的整个生命周期,WindowManagerService负责Activity对应的窗口显示的整个生命周期,两大核心服务各司其职,支撑起了Android Framework的半边天。
       本文主要针对窗口管理及Layer、size计算等逻辑进行分析,窗口显示过程分析可以参考文章:
       Android WMS窗口管理(二)

一.WindowManagerService

       前面说到,WMS是在SystemServer内部启动的,简单看一下启动逻辑:

private void startOtherServices() {
    ...........................
    WindowManagerService wm = null;
    ...........................
    ...........................
    traceBeginAndSlog("StartWindowManagerService");
    // WMS needs sensor service ready
    ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
    mSensorServiceStart = null;
    wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
    ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    traceEnd();
    ........................
}

       可以看到,通过WindowManagerService的main()来创建了对象实例,然后通过ServiceManager将其加入到实名Binder来支持进程间通信。
       看一下main()方法的逻辑实现:

public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
    DisplayThread.getHandler().runWithScissors(() ->
    sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore, policy), 0);
    return sInstance;
}

       内部创建了WMS实例然后返回,看一下WMS的构造方法:

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
    mRoot = new RootWindowContainer(this);
    .................................
    .................................
    mInputManager = inputManager; // Must be before createDisplayContentLocked.
    mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

    mWindowPlacerLocked = new WindowSurfacePlacer(this);
    mPolicy = policy;
    mTaskSnapshotController = new TaskSnapshotController(this);

    LocalServices.addService(WindowManagerPolicy.class, mPolicy);
    .......................................
    mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    mDisplays = mDisplayManager.getDisplays();
    for (Display display : mDisplays) {
        createDisplayContentLocked(display);
    }

    ...................................
    mActivityManager = ActivityManager.getService();
    ......................................
    mAnimator = new WindowAnimator(this);

    LocalServices.addService(WindowManagerInternal.class, new LocalService());
    ..........................
    // Add ourself to the Watchdog monitors.
    Watchdog.getInstance().addMonitor(this);
    .......................
}

       省略了部分逻辑,在构造方法内主要干了如下几件事:
       1.创建RootWindowContainer对象,根窗口容器;
       2.将mPolicy即PhoneWindowManager加入到LocalServices中;
       3.遍历系统所有的Display,创建对应DisplayContent;
       4.将内部LocalService加入到LocalServices中;
       5.加入Watchdog进行监听;
       WindowManagerService内部的其他逻辑在接下来的分析中涉及到的会一一进行讲解;

二.窗口管理

        “窗口”是一个抽象的概念,从用户的角度来讲,其实就是我们看到的“界面”;从WMS的角度来看,它是一个WindowState,用于管理和界面有关的状态;从SurfaceFlinger的角度来看,它是一个layer,承载着和界面有关的数据和属性;在Android中,一个窗口会独占一个Surface,Surface其实就是一个画布,应用程序通过Canvas在Surface上绘制内容;
        本文对从Activity创建到显示的过程进行分析,前面讲到AMS负责Activity的生命周期,WMS负责Activity对应窗口的显示的整个生命周期,两者之间紧密配合,接下来看一下窗口管理涉及的主要类:

1.RootWindowContainer

        在WMS构造方法内进行创建,一个设备对应一个RootWindowContainer,负责管理DisplayContent;

class RootWindowContainer extends WindowContainer<DisplayContent> {
    ................
    private final WindowLayersController mLayersController;
    final WallpaperController mWallpaperController;
    .............................
    RootWindowContainer(WindowManagerService service) {
        mService = service;
        mHandler = new MyHandler(service.mH.getLooper());
        mLayersController = new WindowLayersController(mService);
        mWallpaperController = new WallpaperController(mService);
    }
    ..................
}

        RootWindowContainer继承WindowContainer,是所有窗口管理类的的基类。

2.DisplayContent

        在WMS构造方法内遍历Display进行创建,最终是通过RootWindowContainer进行创建,每个Display对应一个DisplayContent,一个设备可对应多个DisplayContent,创建执行过程就不看了,直接从DisplayContent的构造方法看:

DisplayContent(Display display, WindowManagerService service,
            WindowLayersController layersController, WallpaperController wallpaperController) {

    mDisplay = display;
    mDisplayId = display.getDisplayId();
    mLayersController = layersController;
    mWallpaperController = wallpaperController;
    ..................
    isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
    mService = service;

    // These are the only direct children we should ever have and they are permanent.
    super.addChild(mBelowAppWindowsContainers, null);
    super.addChild(mTaskStackContainers, null);
    super.addChild(mAboveAppWindowsContainers, null);
    super.addChild(mImeWindowsContainers, null);

    // Add itself as a child to the root container.
    mService.mRoot.addChild(this, null);
}

        从构造方法可以看到,在DisplayContent内部管理了4个WindowContainer,简单介绍一下:
       mBelowAppWindowsContainers:NonAppWindowContainers类型,保存了所有应该显示到App类窗口的下面的非App类的窗口,layer设置为0;
        mTackStackContainer: 保存了所有与App(Activities)相关的Window,layer设置为1;
        mAboveAppWindowContainer: NonAppWindowContainer类型,保存了所有应该显示到App类窗口的上面的非App类的窗口,layer设置为2;
        mImeWindowContainers: NonAppWindowContainer类型,包含了所有IME window Containers;
       最终通过mService.mRoot.addChild(this, null)加入到RootWindowContainer内进行管理;
       本文只分析Activity对应的窗口,所以主要涉及mTackStackContainers,mTackStackContainers用来管理TaskStack;

3.TaskStack

        前面讲到,RootWindowContainer和DisplayContent是在WMS的构造方法内部进行创建,从创建时机来看,应该是提前准备好来对窗口进行管理,那TaskStack是什么时候创建的呢?先亮答案:是在ActivityStack创建时进行创建;一起看一下:

ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
            ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
    ............
    mWindowContainerController = createStackWindowController(display.mDisplayId, onTop, mTmpRect2);
    mStackSupervisor.mStacks.put(mStackId, this);
    postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}

       在创建ActivityStack时主要干了几件事:
       1.创建StackWindowController对象;
       2.将新创建的ActiivtyStack加入到ActivityStackSuperVisor的mStacks进行管理;
       3.同时将新创建的ActiivtyStack加入到ActivityDisplay进行管理;

T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
    return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
}

       在创建StackWindowController时,创建了TaskStack;

public StackWindowController(int stackId, StackWindowListener listener,
            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
        super(listener, service);
    mStackId = stackId;

    synchronized (mWindowMap) {
        final DisplayContent dc = mRoot.getDisplayContent(displayId);

        final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
        stack.setController(this);
    }
}

       1.获取displayId对应的DisplayContent;
       2.通过DisplayContent获取TaskStack,在addStackToDisplay()内部先创建TaskStack,然后将其加入到mTaskStackContainers内部进行管理;
       3.设置 TaskStack 的成员变量 mController 为 StackWindowController;
       4.设置 StackWindowController 的成员变量 mContainer 为 TaskStack;

4.Task

       Task是在哪里创建的呢?先亮答案:是在TaskRecord内部进行创建;一起看一下:

void createWindowContainer(boolean onTop, boolean showForAllUsers) {
    final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
    final Configuration overrideConfig = getOverrideConfiguration();
    setWindowContainerController(new TaskWindowContainerController(taskId, this,
            getStack().getWindowContainerController(), userId, bounds, overrideConfig,
            mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
            lastTaskDescription));
}

       跟TaskStack创建过程类似,也是先创建TaskWindowContainerController,然后在其内部创建Task;

public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
            StackWindowController stackController, int userId, Rect bounds,
            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
            boolean homeTask, boolean toTop, boolean showForAllUsers,
            TaskDescription taskDescription, WindowManagerService service) {
    super(listener, service);
    mTaskId = taskId;
    mHandler = new H(new WeakReference<>(this), service.mH.getLooper());

    synchronized(mWindowMap) {
        final TaskStack stack = stackController.mContainer;
        EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
        final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
                    supportsPictureInPicture, homeTask, taskDescription);
        final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
        stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
    }
}

       1.先通过StackWindowController的变量mContainer获取到TaskStack;
       2.通过createTask()来获取Task,并在Task构造方法内部执行setController();
       3.设置 Task 成员变量 mController 为 TaskWindowContainerController;
       4.设置 TaskWindowContainerController 的成员变量 mContainer 为Task;
       5.执行stack.addTask()将Task放入TaskStack进行管理;

5.AppWindowToken

       AppWindowToken是在哪里创建的呢?先亮答案:是在ActivityRecord内部进行创建;一起看一下:

void createWindowContainer() {
    final TaskWindowContainerController taskController = task.getWindowContainerController()

    mWindowContainerController = new AppWindowContainerController(taskController, appToken,
                this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen,
                (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
                appInfo.targetSdkVersion, mRotationAnimationHint,
                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L,
                getOverrideConfiguration(), mBounds);
    .........................
}

       也是先创建AppWindowContainerController,然后在其内部创建AppWindowToken;

public AppWindowContainerController(TaskWindowContainerController taskController,
            IApplicationToken token, AppWindowContainerListener listener, int index,
            int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
            WindowManagerService service, Configuration overrideConfig, Rect bounds) {
    super(listener, service);
    mHandler = new H(service.mH.getLooper());
    mToken = token;
    synchronized(mWindowMap) {
        final Task task = taskController.mContainer;
        atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
                requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
                alwaysFocusable, this, overrideConfig, bounds);
        task.addChild(atoken, index);
    }
}

       1.先通过TaskWindowContainerController的变量mContainer获取到Task;
       2.通过createAppWindow()来获取AppWindowToken,并在AppWindowToken构造方法内部执行setController();
       3.设置 AppWindowToken 成员变量 mController 为 AppWindowContainerController;
       4.设置 AppWindowContainerController 的成员变量 mContainer 为AppWindowToken;
       5.执行task.addChild()将AppWindowToken放入Task进行管理;

6.WindowState

       WindowState对应最终的显示窗口,一般来说,一个Actiivty对应一个WindowState,我们知道,在Activity执行onResume会通过WindowManager.addView()将Activity对应的DecorView进行显示,经过ViewRootImpl-->Session-->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) {
    .........................
    WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
    atoken = token.asAppWindowToken();
    .........................
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
    .................
    win.mToken.addWindow(win);
    ................
}

       1.根据参数获取到对应的AppWindowToken(前面已经创建了);
       2.将token作为参数传入创建WindowState;
       3.将WindowState加入到AppWindowToken内部进行管理;

7.总结

       前面逻辑处理涉及到AMS,用一张图来总结一下相互之间的逻辑关系:


image.png

三.窗口显示Layer分配

       不同的窗口对应的不同的显示layer,比如状态栏要显示在应用窗口之上,所以需要提前分配好要显示的layer,然后通知给SurfaceFlinger,最后在绘制合成时保证显示正常;
       前面说到,WindowState对应最终的显示窗口,所以layer应该是在WindowState内部体现,一起看一下:

1.WindowState

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
    .....................
    .....................

    if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
        mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
        mIsChildWindow = true;
        parentWindow.addChild(this, sWindowSubLayerComparator);
        mLayoutAttached = mAttrs.type !=
                    WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
         mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
                    || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
         mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
    } else {
        mBaseLayer = mPolicy.getWindowLayerLw(this)* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        mSubLayer = 0;
        mIsChildWindow = false;
        mLayoutAttached = false;
        mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
                    || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
        mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
    }
    .........................

    mWinAnimator = new WindowStateAnimator(this);
    ..........................
    mLayer = 0;
}

       1.mBaseLayer:基础layer,由窗口类型(通过policy转换后)×10000+1000计算得到,相同type对应WindowState的mBaseLayer都是一样的,窗口类型对应区间如下:
              应用窗口:层级范围是1~99
              子窗口:层级范围是1000~1999
              系统窗口:层级范围是2000~2999
       2.mLayer:经过计算得到最终的窗口显示layer值;
       3.创建WindowState对应的WindowStateAnimator,并初始化将mLayer设置为0;
       在创建WindowState进行一些基本计算,那最终的值是在什么地方确定的呢?
       还得回到WMS的addWindow()方法,在创建完WindowState后,会执行以下逻辑:

displayContent.assignWindowLayers(false /* setLayoutNeeded */)

       根据调用关系,会执行到WindowLayersController的assignWindowLayers()方法:

2.WindowLayersController

final void assignWindowLayers(DisplayContent dc) {
    reset();
    dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
    adjustSpecialWindows();
}
2.1.reset()
private void reset() {
    mPinnedWindows.clear();
    mInputMethodWindows.clear();
    .............

    mCurBaseLayer = 0;
    mCurLayer = 0;
    mAnyLayerChanged = false;

    mHighestApplicationLayer = 0;
    mHighestDockedAffectedLayer = 0;
    mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
    mImeTarget = mService.mInputMethodTarget;
    mAboveImeTarget = false;
    mAboveImeTargetAppWindows.clear();
}

       将mCurBaseLayer和mCurLayer等置为0;

2.2.mAssignWindowLayersConsumer

private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
    boolean layerChanged = false;

    int oldLayer = w.mLayer;
    if (w.mBaseLayer == mCurBaseLayer) {
        mCurLayer += WINDOW_LAYER_MULTIPLIER;
    } else {
        mCurBaseLayer = mCurLayer = w.mBaseLayer;
    }
    assignAnimLayer(w, mCurLayer);

    if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
         layerChanged = true;
         mAnyLayerChanged = true;
    }

    if (w.mAppToken != null) {
        mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
                    w.mWinAnimator.mAnimLayer);
    }
    if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
        mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
                    w.mWinAnimator.mAnimLayer);
    }
    if (w.getAppToken() != null && StackId.isResizeableByDockedStack(w.getStackId())) {
        mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
                    w.mWinAnimator.mAnimLayer);
    }

    collectSpecialWindows(w);

    if (layerChanged) {
        w.scheduleAnimationIfDimming();
    }
};

       mCurBaseLayer: 记录当前计算的对应type窗口的baseLayer;mCurLayer: 最终需要设置的layer值;
       通过处理逻辑可以看到,相同baseLayer的窗口会每次加WINDOW_LAYER_MULTIPLIER(5),即:相同type的窗口layer值之间呈+5递增;

2.2.1.assignAnimLayer(w, mCurLayer)
private void assignAnimLayer(WindowState w, int layer) {
    w.mLayer = layer;
    w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment() + w.getSpecialWindowAnimLayerAdjustment();
       ....................
}

       通过assignAnimLayer()将刚才计算得到的mCurLayer设置给WindowState的mLayer;如果是App窗口,mAnimLayer与mLayer值是相同的;

2.2.2. collectSpecialWindows(w)
private void collectSpecialWindows(WindowState w) {
    ...........................
    if (w.mIsImWindow) {
        mInputMethodWindows.add(w);
        return;
    }
       

    final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID;
    if (stackId == PINNED_STACK_ID) {
        mPinnedWindows.add(w);
    }
    ..........
}

       收集特殊的窗口,比如:输入法、画中画窗口等;

2.3.adjustSpecialWindows()

private void adjustSpecialWindows() {
    int layer = mHighestDockedAffectedLayer +  TYPE_LAYER_OFFSET;
    ..........................
    layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER)
    ............................
    while (!mInputMethodWindows.isEmpty()) {
        layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
    }
}

private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
    if (win != null) {
        assignAnimLayer(win, layer);
        // Make sure we leave space in-between normal windows for dims and such.
        layer += WINDOW_LAYER_MULTIPLIER;
    }
    return layer;
}

       最后调整特殊窗口的layer值;调整完后在什么地方来设置到SurfaceFlinger呢?后续会讲到。

四.窗口size计算

       在窗口显示前需要先知道要显示窗口的大小及位置,那么这里就要用到窗口大小及位置计算,接下来先看一下系统区域大小定义:
       overScan:边缘区域(四周有一圈黑色的区域),该区域是显示屏的一部分,但通常不显示画面;
       overscanScreen:包含了overscan区域,相当于整个屏幕大小;
       RestrictedOverscanScreen:不包含导航栏;
       RestrictedScreen:不包含屏幕上的overscan区域,不包含导航条;
       UnrestrictedScreen:不包含overscan区域,包含状态条和导航栏;
       StableFullScreen:包含状态栏,不包含导航栏;
       Decor:不包含状态栏,不包含导航栏,包含输入法的区域;
       Current:不包含状态栏,不包含导航栏,不包含输入法的区域;
       我们知道,窗口在显示时在ViewRootImpl内会调用到relayoutWindow(),最终会调用到WMS的relayoutWindow(),在内部会执行到以下逻辑:

mWindowPlacerLocked.performSurfacePlacement(true /* force */)

       跟着调用关系会执行到DisplayContent的applySurfacecChangesTransaction(),内部会调用到performLayout():

void performLayout(boolean initial, boolean updateInputWindows) {
    .............................
    final int dw = mDisplayInfo.logicalWidth;
    final int dh = mDisplayInfo.logicalHeight;

    mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation,
                getConfiguration().uiMode);
    ..................
    mService.mPolicy.getContentRectLw(mContentRect);
    ..................
    // First perform layout of any root windows (not attached to another window).
    forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
    .......................
}

1.beginLayoutLw()

       获取到Display的宽度和高度,调用PhoneWindowManager的beginLayoutLw()来初始化系统区域大小(上面提到的)以及计算其他值;

public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
                              int displayRotation, int uiMode) {
    .......................
    mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
    mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
    mSystemRight = displayWidth;
    mSystemBottom = displayHeight;
    mUnrestrictedScreenLeft = overscanLeft;
    mUnrestrictedScreenTop = overscanTop;
    mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
    mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
    ...............................
    final Rect pf = mTmpParentFrame;
    final Rect df = mTmpDisplayFrame;
    final Rect of = mTmpOverscanFrame;
    final Rect vf = mTmpVisibleFrame;
    final Rect dcf = mTmpDecorFrame;
    pf.left = df.left = of.left = vf.left = mDockLeft;
    pf.top = df.top = of.top = vf.top = mDockTop;
    pf.right = df.right = of.right = vf.right = mDockRight;
    pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
    dcf.setEmpty(); 
    .................................
    boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
                    displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,navAllowedHidden, statusBarExpandedNotKeyguard);
    updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
}

       pf(parentFrame):表示窗口父窗口的大小;
       df(deviceFrame):设备的屏幕大小;
       of(OverScanFrame):设备屏幕大小、和df值一样;
       cf(ContentFrame):窗口内容区域大小;
       vf(VisibleFrame):窗口可见内容区域大小;
       dcf(DecorFrame):装饰区域大小,除去状态栏和导航栏的区域;
       接着看一下layoutStatusBar()实现:

private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
            boolean isKeyguardShowing) {
    if (mStatusBar != null) {
         // apply any navigation bar insets
         pf.left = df.left = of.left = mUnrestrictedScreenLeft;
         pf.top = df.top = of.top = mUnrestrictedScreenTop;
         pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
         pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
                    + mUnrestrictedScreenTop;
         vf.left = mStableLeft
         vf.top = mStableTop;
         vf.right = mStableRight;
         vf.bottom = mStableBottom;
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                    vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
                    dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */)
         ..................
         mContentTop = mVoiceContentTop = mCurTop = mDockTop;
         mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
         mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
         mContentRight = mVoiceContentRight = mCurRight = mDockRight;
    }
}

       在计算完pf、df等值时,调用到WindowState的computeFrameLw():

public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
            Rect outsetFrame) {
    ...................
    mOverscanFrame.set(overscanFrame);
    mContentFrame.set(contentFrame);
    mVisibleFrame.set(visibleFrame);
    mDecorFrame.set(decorFrame);
    mStableFrame.set(stableFrame);
    ....................
    mContainingFrame.set(parentFrame);
    mDisplayFrame.set(displayFrame);
    mFrame.set(left, top, left + width, top + height);
    ..................
}

       在computeFrameLw()内部主要是设置了以下几个变量:
       mFrame:描述窗口的位置和尺寸;
       mContainingFrame和mParentFrame:这两个矩形相同,保存了pf参数;
       mDisplayFrame:保存了df参数;
       mContentFrame和mContentInsets:mContentFrame表示当前窗口中显示内容的区域,mContentInsets表示mContentFrame与mFrame的4条边界之间的距离;
       mVisibleFrame和mVisibleInsets:mVisibleFrame表示当前不被系统遮挡的区域,mVisibleInsets表示mVisibleFrame与mFrame的4条边界之间的距离;

2.layoutWindowLw()

       在DisplayContent内部的performLayout()会执行到以下逻辑:

forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

       会遍历WindowState执行:

mService.mPolicy.layoutWindowLw(w, null);

       执行到PhoneWindowManager的layoutWindowLw()方法,一起看一下:

public void layoutWindowLw(WindowState win, WindowState attached) {
    //StatusBar和NavigationBar在beginLayoutLw()内已经处理了 
    if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
        return;
    }
    final WindowManager.LayoutParams attrs = win.getAttrs();
    ................
    if (attrs.type == TYPE_INPUT_METHOD) {
    } else if (attrs.type == TYPE_WALLPAPER) {
        layoutWallpaper(win, pf, df, of, cf);
    } else {
       // Default policy decor for the default display
        dcf.left = mSystemLeft;
        dcf.top = mSystemTop;
        dcf.right = mSystemRight;
        dcf.bottom = mSystemBottom;
        final boolean isAppWindow =
                attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
                attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
       ................
    }
    ....................
    win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf)
    .......................
}

       循环遍历WindowState设置显示大小及位置;
       窗口显示Layer已经分配好,大小和位置也已经设置好,接下来就轮到显示了,显示流程请参考下篇文章Android WMS窗口管理(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容