美文网首页
Android WMS窗口管理

Android WMS窗口管理

作者: 雷涛赛文 | 来源:发表于2022-01-20 16:28 被阅读0次

           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窗口管理(二)

    相关文章

      网友评论

          本文标题:Android WMS窗口管理

          本文链接:https://www.haomeiwen.com/subject/mmbrhrtx.html