当前位置: 移动技术网 > 移动技术>移动开发>Android > performTraversal调用relayoutWindow计算窗口大小

performTraversal调用relayoutWindow计算窗口大小

2020年08月02日  | 移动技术网移动技术  | 我要评论
performTraversal调用relayoutWindow方法分析在performTraversal,会涉及到View的measure、layout、draw。其中measure用来对View进行测量,给出建议值,layout来确定子控件在父控件中的位置,包括真实大小以及坐标位置,draw负责将View绘制出来。单文只分析与窗口大小相关的逻辑,performTraversal方法会被多次调用到,这个方法是计算窗口大小的起点窗口区域Overscan(过扫描区):   Ov..

performTraversal调用relayoutWindow方法分析

在performTraversal,会涉及到View的measure、layout、draw。其中measure用来对View进行测量,给出建议值,layout来确定子控件在父控件中的位置,包括真实大小以及坐标位置,draw负责将View绘制出来。单文只分析与窗口大小相关的逻辑,performTraversal方法会被多次调用到,这个方法是计算窗口大小的起点

窗口区域



Overscan(过扫描区):

Overscan是电视机特有的概念,上图中黄色的区域就是overscan区域,指的是电视机四周一圈的黑色区域,称为overscan(过扫描)区域,这个区域也是显示屏的一部分,但通常不能显示。如果窗口的某些内容画在这个区域里,它在某些电视上就会看不到。为了避免这种情况发生,通常要求UI不要画在屏幕的边角上,而是预留一定的空间。因为Overscan的区域大小随着电视不 同而不同,它一般由终端用户通过指定。注意:手机硬件上不存在这个区域。

OverscanScreen/Screen:

OverscanScreen是包含Overscan区域的屏幕大小,而Screen则为去除Overscan区域后的屏幕区域,OverscanScreen > Screen。

Restricted and Unrestricted:

某些区域是被系统保留的,比如说手机屏幕上方的状态栏(上图绿色区域)和下方的导航栏,根据是否包括这些预留的区域,Android把区域分为Unrestricted Area 和 Resctrited Aread,前者包括这部分预留区域,后者不包含,故Unrestricted area > Rectricted area。


RestrictedOverScanScreen:

包括overscan区,不包含导航栏、因此这个区域上面到屏幕的顶部,下面就到导航栏的顶部。

l RestrictedScreen:

这个区域不包含overscan区域不包含导航栏。

l UnRestrictedScreen:

不包含屏幕的overscan区域、包含状态栏和导航栏。

l stableFullScreen:

包含状态栏、输入法、不包含导航栏。

l Decor区域:

DecorView大小。包含输入法区域,包含状态栏/导航栏区域与否,与Window Flag有关。

l Current区域/Visible区域:

可视区域。不包含状态栏、导航栏和输入法区域。getWindowVisibleDisplayFrame获得的Rect大小。

在大多数情况下,Activity窗口的内容区域和可见区域的大小是一致的,而状态栏和输入法窗口所占用的区域又称为屏幕装饰区(decorations)。WMS服务实际上就是需要根据屏幕以及可能出现的状态栏和输入法窗口的大小来计算出Activity窗口的整体大小及其过扫描区域边衬和可见区域边衬的大小。

ViewRootImpl中和窗口相关的变量和方法

int desiredWindowWidth; 期望的宽度值 int desiredWindowHeight; 期望的高度值

// 当Activity的窗口大小需要改变时,WMS会通过W.resized接口来通知客户端,mWinFrame用来记录WMS提供的宽高 final Rect mWinFrame; // frame given by window manager.

final Rect mPendingVisibleInsets = new Rect();//wms提供的,表示可见区域 final Rect mPendingContentInsets = new Rect();//wms提供的,表示内容区域

ContentInsets:内容区域 ,VisibleInsets :可见区域

短信编辑界面:当输入法窗口出现时,应用窗口变小以容纳软键盘,这是内容区域和可见区域显示一样

联系人编辑界面:当输入法窗口出现时,应用窗口没有发生变化,软键盘覆盖了一部分,这是内容区域和可见区域显示不一样

分析一下perfromTraversal中与窗口计算相关的代码,分段分析perfromTraversal

 private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;//当前window的Decorview


        int desiredWindowWidth;
        int desiredWindowHeight;
        //将mWinFrame赋值给了局部变量frame,mWinFrame保存的为WMS修改后的window的宽度和高度
        Rect frame = mWinFrame;
        if (mFirst) {//第一次绘制
            mFullRedrawNeeded = true;//需要全部绘制
            mLayoutRequested = true;//需要全部布局

            final Configuration config = mContext.getResources().getConfiguration();
            if (shouldUseDisplaySize(lp)) {
                //如果是statusbarpanel,音量调节窗口,输入法窗口,获得尺寸为屏幕分辨率的真实尺寸
                Point size = new Point();
                mDisplay.getRealSize(size);
                desiredWindowWidth = size.x;
                desiredWindowHeight = size.y;
            } else {
            //否在将mWinFrame大小赋值给desiredWindowWidth
                desiredWindowWidth = mWinFrame.width();
                desiredWindowHeight = mWinFrame.height();
            }
........
        } else {
        //如果不是第一次绘制的,desiredWindowWidth/desiredWindowHeight就为mWindowFrame/frame的宽度和高度
            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
            //mWidth此时表示上次执行该方法时frame.width(),如果这两个值不相等,说明此视图需要重新绘制
            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                mFullRedrawNeeded = true;
                mLayoutRequested = true;
                windowSizeMayChange = true;//window尺寸发生变化
            }
        }

这段代码用来获得窗口的当前宽度和desiredWindowWidth和当前高度desiredWindowHeight。

这段代码有一个全局变量mWinFrame,用来保存窗口当前高度和宽度,一个局部变量frame,绘制前将当前窗口大小mWinFrame赋值给frame。另外两个成员变量mWidth和mHeight也用来描述窗口当前的宽度和高度,但是它们与desiredWindowWidth/desiredWindowHeight不同,它保存的是应用程序进程上一次主动请求wms计算得到的,并且会一直保持不变到应用程序进程下一次再请求WindowManagerService服务来重新计算为止。

Activity窗口第一次被请求执行测量、布局和绘制操作,成员变量mFirst为true,如果当前窗口是statusbar窗口、输入法窗口或者音量调节窗口,那么它的当前宽度desiredWindowWidth和当前高度desiredWindowHeight就等于屏幕实际大小,否则的话,desiredWindowWidth/ desiredWindowHeight等于保存在全局变量mWinFrame中的宽度和高度值。

如果Activity窗口不是第一次被请求执行测量、布局和绘制操作,并且mWidth和高度mHeight不等于Activity窗口的当前宽度desiredWindowWidth和当前高度desiredWindowHeight,那么就说明Activity窗口的大小发生了变化,这时候变量windowSizeMayChanged的值就会被标记为true,以便接下来可以对窗口的大小变化进行处理。

 boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
        
        if (layoutRequested) {
           ........
            // Ask host how big it wants to be
           //通过measureHierarchy来对viewtree进行一次计算
           windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
        }
 .......
         if (layoutRequested) {
            mLayoutRequested = false;
        } 

注意:当第一次请求绘制时,mLayoutRequested为true,并且请求绘制的窗口不在stop状态,layoutRequested也为true,的情况下调用measureHierarchy进行第一次view tree的计算。这次计算会确定顶层视图,即window的大小,然后返回的window大小是否改变的布尔值。接着会把mLayoutRequested设置为false,这就说明了measureHierarchy只会被调用一次

 if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            mForceNextWindowRelayout = false;

           ........

                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

                if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
                        + " overscan=" + mPendingOverscanInsets.toShortString()
                        + " content=" + mPendingContentInsets.toShortString()
                        + " visible=" + mPendingVisibleInsets.toShortString()
                        + " stable=" + mPendingStableInsets.toShortString()
                        + " cutout=" + mPendingDisplayCutout.get().toString()
                        + " outsets=" + mPendingOutsets.toShortString()
                        + " surface=" + mSurface);

满足下面的条件之一进入if判断:

  1. mFirst等于true,即第一次执行测量、布局和绘制操作

  2. windowShouldResize等于true,即窗口的大小发生了变化。

  3. insetsChanged等于true,即窗口的内容区域边衬发生了变化。

  4. viewVisibilityChanged等于true,窗口可见性发生了变化。

  5. params不为空,窗口的属性发生了变化,指向了一个WindowManager.LayoutParams对象。

在满足上述条件之一,并且Activity窗口处于可见状态,那么就需要检查接下来请求WindowManagerService服务计算大小时,是否要告诉WindowManagerService服务它指定了额外的内容区域边衬和可见区域边衬,即insetsPending是否为true。接下来调用成员函数relayoutWindow请求WindowManagerService计算窗口的大小以及内容区域边衬大小和可见区域边衬大小。

relayoutWindow

Session.relayout

@Override
    public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
            Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
            Surface outSurface) {

       int res = mService.relayoutWindow(this, window, seq, attrs,
               requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
               outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outBackdropFrame, cutout,
                mergedConfiguration, outSurface);



   }

outFrame:WMS得出的应用窗口大小,对应ViewRootImpl的mWinFrame

outOverscanInsets:WMS得出的过扫描区域

outContentInsets:WMS得出的ContentInsets,对应ViewRootImpl的mPendingContentInsets

outVisibleInsets:WMS得出的VisibleInsets,对应ViewRootImpl的mPendingVisileInsets

outStableInsets:WMS的得出的StableInsets,对应ViewRootImpl的

outSurface:WMS申请的有效的Surface对象,对应ViewRootImpl的mSurface

Displaycontent.performLayout

WMS计算布局最终会调用到DispalyContent.performLayout方法,这个方法是从WindowSurfacePlacer.performSurfacePlacement中通过层层调用的,本文主要介绍确认窗口大小的相关知识,其他的方法先不做介绍,调用逻辑如下:


Displaycontent.applySurfaceChangesTransaction

而在调用此方法的时候会遍例当前屏幕所有的DisplayContent的信息,此时如果通过DisplayConten.getDisplayInfo的信息后,此时获得相关的逻辑尺寸的信息与物理尺寸是有区别的,例如通过修改屏幕的密度和size的信息,此时得到为修改后的屏幕的信息不是实际屏幕的物理尺寸.而默认的屏幕用Display.DEFAULT_DISPLAY来作为标记

 boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
    final int dw = mDisplayInfo.logicalWidth;
    final int dh = mDisplayInfo.logicalHeight;
    final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;

    mTmpUpdateAllDrawn.clear();

    int repeats = 0;
    do {//退出的条件是pendingLayoutChanges==0,即布局已经完成没有变化
        repeats++;
        if (repeats > 6) {//最多循环六次
            clearLayoutNeeded();
            break;
        }

        if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
                pendingLayoutChanges);
        //这里只修改了默认屏幕的WallpaperWindow的相关信息
        if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
            mWallpaperController.adjustWallpaperWindows(this);
        }

        if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {     //重新计算config
            if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
                setLayoutNeeded();
                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
            }
        }
        //需要重新计算layout
        if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            setLayoutNeeded();
        }

        //这个方法最多被调用四次
        if (repeats < LAYOUT_REPEAT_THRESHOLD) {
            performLayout(repeats == 1, false /* updateInputWindows */);
        } else {
            Slog.w(TAG, "Layout repeat skipped after too many iterations");
        }

pendingLayoutChanges = 0; //如果为了支持多屏显示,isDefaultDisplay需要被删除 if (isDefaultDisplay) { mService.mPolicy.beginPostLayoutPolicyLw(dw, dh); forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */); pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( "after finishPostLayoutPolicyLw", pendingLayoutChanges); } } while (pendingLayoutChanges != 0);

 mTmpApplySurfaceChangesTransactionState.reset();

    mTmpRecoveringMemory = recoveringMemory;
    //将所有窗口的改变设置给surface
    forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
    //准备surface
    prepareSurfaces();

//向DMS发送相关的 DisplayProperties属性的变化 mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
            mTmpApplySurfaceChangesTransactionState.displayHasContent,
            mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
            mTmpApplySurfaceChangesTransactionState.preferredModeId,
            true /* inTraversal, must call performTraversalInTrans... below */);
    //wallpaper的可见性是否发生了变化
    final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
    if (wallpaperVisible != mLastWallpaperVisible) {
        mLastWallpaperVisible = wallpaperVisible;
        mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
    }

    while (!mTmpUpdateAllDrawn.isEmpty()) {
        final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
        // See if any windows have been drawn, so they (and others associated with them)
        // can now be shown.
        atoken.updateAllDrawn();
    }

    return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
}

此方法的主要工作如下:

1.根据pendingLayoutChanges的值是否为0,来执行一个do-while大循环,而此循环体主要分了三个阶段:

pendingLayoutChanges的处理阶段,根据不同的值做相关的功能处理

布局阶段,调用performLayout方法来对DisplayContent下的所有的窗口进行布局,布局完成后将pendingLayoutChanges变为0

结果检查阶段,当所有的窗口的布局完成后,通过一下状态量的检查,决定是否需要重复以上上个阶段的工作,其检查的内容主要是状态烂,导航栏可见性是否与顶层窗口的属性冲突,是否需要解锁状态等,从而再重新pendingLayoutChanges的值后,看是否继续进入循环

2.完成DisplayContent相关布局后的处理

pendingLayoutChanges 的可选值:

//layout状态可能已经改变,所以要发起一次layout
int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
//config状态发生变化
int FINISH_LAYOUT_REDO_CONFIG = 0x0002;
//壁纸可能需要移动
int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
//需要重新计算动画
int FINISH_LAYOUT_REDO_ANIM = 0x0008;

performLayout

 void performLayout(boolean initial, boolean updateInputWindows) {
       //如果此变量为false直接返回,不需要layout,如果需要刷新数据时,必须将此值设置为true
        if (!isLayoutNeeded()) {
            return;
        }
        //已经执行了本次的layout将layoutNeeded设置为false
        clearLayoutNeeded();

        final int dw = mDisplayInfo.logicalWidth;
        final int dh = mDisplayInfo.logicalHeight;
  //先获得DisplayFrame的值,当前phoneWindow上的信息      mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
        mDisplayFrames.mRotation = mRotation;
//初始化        mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
        //这里只设置默认屏
        if (isDefaultDisplay) {
            // Not needed on non-default displays.
            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
            mService.mScreenRect.set(0, 0, dw, dh);
        }
        //用于与客户端同步
        int seq = mLayoutSeq + 1;
        if (seq < 0) seq = 0;//当seq超过范围需要重新从0开始计算
        mLayoutSeq = seq;

        // Used to indicate that we have processed the dream window and all additional windows are
        // behind it.
        mTmpWindow = null;
        mTmpInitial = initial;

        //计算父窗口的大小
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

        // Used to indicate that we have processed the dream window and all additional attached
        // windows are behind it.
        mTmpWindow2 = mTmpWindow;
        mTmpWindow = null;

        //如果存在子窗口将计算子窗口的大小,如popupwindow
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);

        // Window frames may have changed. Tell the input dispatcher about it.
    //告诉InputDispatch,window的窗口可能发生了变化    mService.mInputMonitor.layoutInputConsumers(dw, dh);
        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
        if (updateInputWindows) {
            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
        }

   //更新分屏窗口的信息     mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
    }

PhoneWindowManager.beginLayoutLw

 @Override
    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
        //设置相关区域的初始值overScan,Dock,mContent等
        displayFrames.onBeginLayout();

        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
        mDockLayer = 0x10000000;
        mStatusBarLayer = -1;
        //将在pf,df等设置为(0,0,屏幕宽度,屏幕高度)
        // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
        final Rect pf = mTmpParentFrame;
        final Rect df = mTmpDisplayFrame;
        final Rect of = mTmpOverscanFrame;
        final Rect vf = mTmpVisibleFrame;
        final Rect dcf = mTmpDecorFrame;
        vf.set(displayFrames.mDock);
        of.set(displayFrames.mDock);
        df.set(displayFrames.mDock);
        pf.set(displayFrames.mDock);
        dcf.setEmpty();  // Decor frame N/A for system bars.
        //这里只涉及默认屏幕的相关信息
        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {

            final int sysui = mLastSystemUiFlags;
            //navbar是否可见,查看应用是否设置了SYSTEM_UI_FLAG_HIDE_NAVIGATION属性
            boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
            //nav的透明属性
            boolean navTranslucent = (sysui
                    & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
            //查看当前是否为沉浸模式,看看应用是否设置了SYSTEM_UI_FLAG_IMMERSIVE或者SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
            boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
            //如果设置了沉浸模式,navbar会被要求隐藏
            boolean navAllowedHidden = immersive || immersiveSticky;
            navTranslucent &= !immersiveSticky;  // transient trumps translucent
            //判断keyguard是否显示
            boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded;
            if (!isKeyguardShowing) {
                navTranslucent &= areTranslucentBarsAllowed();
            }
            //statusbar处于下拉状态非锁屏状态:PRIVATE_FLAG_STATUS_BAR_EXPANDED
            boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null
                    && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0;

      
            //根据navbar的可见性和透明度等信息对其进行布局
            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
                    navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
            if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
            //对statusbar进行布局
            updateSysUiVisibility |= layoutStatusBar(
                    displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
            if (updateSysUiVisibility) {
                updateSystemUiVisibilityLw();
            }
        }
        layoutScreenDecorWindows(displayFrames, pf, df, dcf);
        //布局刘海的相关信息
        if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
            // Make sure that the zone we're avoiding for the cutout is at least as tall as the
            // status bar; otherwise fullscreen apps will end up cutting halfway into the status
            // bar.
            displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
                    displayFrames.mStable.top);
        }
    }

beginLayoutLw方法的作用就是先将这些变量进行初始化,先初始化为全屏,接下来看是否存在状态栏和导航栏,导航栏:竖屏在下方,横屏在右方 。状态栏的位置相对固定

layoutNavigationBar

private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
            boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
            boolean statusBarExpandedNotKeyguard) {
        //如果不存在NavigationBar(TYPE_NAVIGATION_BAR),直接返回,car当左边显示的为TYPE_NAVIGATION_BAR_PANEL,有可能不会执行此代码逻辑
        if (mNavigationBar == null) {
            return false;
        }
        //查看NavBar是否显示
        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
        //根据屏幕的方向,最终确认NavBar的显示位置
        final int rotation = displayFrames.mRotation;
        final int displayHeight = displayFrames.mDisplayHeight;
        final int displayWidth = displayFrames.mDisplayWidth;
        final Rect dockFrame = displayFrames.mDock;
        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
    
        final Rect cutoutSafeUnrestricted = mTmpRect;
        cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
        cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
        //如果navbar为底部显示模式
        if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
            // It's a system nav bar or a portrait screen; nav bar goes on bottom.
            //top一般为整个屏幕的尺寸除去NavigationBarHeight,一般根据资源配置中的                  //res.getDimensionPixelSize(R.dimen.navigation_bar_height)获得相关的值
            final int top = cutoutSafeUnrestricted.bottom
                    - getNavigationBarHeight(rotation, uiMode);
            //此时设置navbar的window大小为
            mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
            //设置mStable的buttom值为navbar的top值,即除去navbar的高度
            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
            if (transientNavBarShowing) {
                mNavigationBarController.setBarShowingLw(true);
            } else if (navVisible) {
                mNavigationBarController.setBarShowingLw(true);
                //navbar显示,dockFrame的大小也是除去导航栏的高度
                dockFrame.bottom = displayFrames.mRestricted.bottom
                        = displayFrames.mRestrictedOverscan.bottom = top;
            } else {
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                //根据statusbar的状态来设置navigationbar的显示状态
                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
            }
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) {
                //如果满足如上条件,这mSystem的区域也是除去导航栏的高度
                displayFrames.mSystem.bottom = top;
            }
        } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {//导航栏在右边的情况,即横屏。车机的情况需要确认其方向。
            // Landscape screen; nav bar goes to the right.
            final int left = cutoutSafeUnrestricted.right
                    - getNavigationBarWidth(rotation, uiMode);
            mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
            displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
            if (transientNavBarShowing) {
                mNavigationBarController.setBarShowingLw(true);
            } else if (navVisible) {
                mNavigationBarController.setBarShowingLw(true);
                dockFrame.right = displayFrames.mRestricted.right
                        = displayFrames.mRestrictedOverscan.right = left;
            } else {
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
            }
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) {
                // If the nav bar is currently requested to be visible, and not in the process of
                // animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.right = left;
            }
        } else if (mNavigationBarPosition == NAV_BAR_LEFT) {//横屏,导航栏左显示
            // Seascape screen; nav bar goes to the left.
        ........
        }

        //根据navbar的显示情况来更新mCurrent,mVoiceContent,mContent的区域
        displayFrames.mCurrent.set(dockFrame);
        displayFrames.mVoiceContent.set(dockFrame);
        displayFrames.mContent.set(dockFrame);
        mStatusBarLayer = mNavigationBar.getSurfaceLayer();
        //mNavigationBar表示navbar的windowstate,通过调用windowstate.computeFrameLw方法来确认最中的   
       //NavigationFrame的大小
        mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
        //将计算的结果设置给navigationBar
        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
        
        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
        return mNavigationBarController.checkHiddenLw();
    } 

layoutStatusBar

private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
            Rect dcf, int sysui, boolean isKeyguardShowing) {
        // type:TYPE_STATUS_BAR的windowstate
        if (mStatusBar == null) {
            return false;
        }
        // apply any navigation bar insets,可能为减去导航栏高度的区域
        of.set(displayFrames.mUnrestricted);
        df.set(displayFrames.mUnrestricted);
        pf.set(displayFrames.mUnrestricted);
        vf.set(displayFrames.mStable);
        //获得statusbar的surfaceLayer
        mStatusBarLayer = mStatusBar.getSurfaceLayer();

        //调用windowstate的computeFrameLw方法,计算statusbar的frame
        mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);

        // mStable的top值一般为statusbar的高度值
        displayFrames.mStable.top = displayFrames.mUnrestricted.top
                + mStatusBarHeightForRotation[displayFrames.mRotation];
        // Make sure the status bar covers the entire cutout height
        displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
                displayFrames.mDisplayCutoutSafe.top);

        // Tell the bar controller where the collapsed status bar content is
        mTmpRect.set(mStatusBar.getContentFrameLw());
        mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
        mTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
        mTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
        mStatusBarController.setContentFrame(mTmpRect);

        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
        boolean statusBarTranslucent = (sysui
                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
        if (!isKeyguardShowing) {
            statusBarTranslucent &= areTranslucentBarsAllowed();
        }

        // If the status bar is hidden, we don't want to cause windows behind it to scroll.
        if (mStatusBar.isVisibleLw() && !statusBarTransient) {
            // Status bar may go away, so the screen area it occupies is available to apps but just
            // covering them when the status bar is visible.
            final Rect dockFrame = displayFrames.mDock;
            dockFrame.top = displayFrames.mStable.top;
            displayFrames.mContent.set(dockFrame);
            displayFrames.mVoiceContent.set(dockFrame);
            displayFrames.mCurrent.set(dockFrame);
            //根据statusbar的相关属性,设置content  dockFrame mContent的值
            if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
                    "dock=%s content=%s cur=%s", dockFrame.toString(),
                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));

            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
                    && !mStatusBarController.wasRecentlyTranslucent()) {
                // If the opaque status bar is currently requested to be visible, and not in the
                // process of animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.top = displayFrames.mStable.top;
            }
        }
        return mStatusBarController.checkHiddenLw();
    }


layoutWindowLw

无论是 mPerformLayout,还是mPerformLayoutAttached最后都会调用到layoutWindowLw,

 @Override
    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
        // We've already done the navigation bar, status bar, and all screen decor windows. If the
        // status bar can receive input, we need to layout it again to accommodate for the IME
        // window.
        //如果窗口为statusbar而且不能接收输入事件的情况下,或者navbar,或者系统区域已经包含此窗口的,就不需要重新layout,
        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
                || mScreenDecorWindows.contains(win)) {
            return;
        }
        final WindowManager.LayoutParams attrs = win.getAttrs();
        //获得默认屏幕
        final boolean isDefaultDisplay = win.isDefaultDisplay();
        final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
                (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
        if (needsToOffsetInputMethodTarget) {
            if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
            offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
        }
       
        final int type = attrs.type;//窗口的type类型
        final int fl = PolicyControl.getWindowFlags(win, attrs);//窗口的flag
        final int pfl = attrs.privateFlags;//窗口的private的flags
        final int sim = attrs.softInputMode;//输入法窗口的模式
        final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);//systemui
        final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
        final Rect pf = mTmpParentFrame;//父窗口区域大小
        final Rect df = mTmpDisplayFrame;//显示区域大小
        final Rect of = mTmpOverscanFrame;//过扫描区域大小
        final Rect cf = mTmpContentFrame;//内容区域大小
        final Rect vf = mTmpVisibleFrame;//可见区域大小
        final Rect dcf = mTmpDecorFrame;//Decor区域
        final Rect sf = mTmpStableFrame;//固定区域
        Rect osf = null;
        dcf.setEmpty();


        //是否包含navabr的,这里只包含默认显示屏
        final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
                && mNavigationBar != null && mNavigationBar.isVisibleLw());
        //输入法窗口的模式
        final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
       //是否需要全屏显示,应用是否设置了FLAG_FULLSCREEN,SYSTEM_UI_FLAG_FULLSCREEN的flag
        final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
                || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
        //获得layoutInScreen与layoutInsetDecor的值
        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
        final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
        //将mStable设置给sf
        sf.set(displayFrames.mStable);
        //输入法窗口的设置,显示在导航栏的上方,其window的大小为减去导航栏的高度,设置为mDock区域大小
        if (type == TYPE_INPUT_METHOD) {
            vf.set(displayFrames.mDock);
            cf.set(displayFrames.mDock);
            of.set(displayFrames.mDock);
            df.set(displayFrames.mDock);
            pf.set(displayFrames.mDock);
            // IM dock windows layout below the nav bar...
            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
            // ...with content insets above the nav bar
            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                // The status bar forces the navigation bar while it's visible. Make sure the IME
                // avoids the navigation bar in that case.
                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
                    pf.right = df.right = of.right = cf.right = vf.right =
                            displayFrames.mStable.right;
                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
                }
            }
            // IM dock windows always go to the bottom of the screen.
            attrs.gravity = Gravity.BOTTOM;
            mDockLayer = win.getSurfaceLayer();
        } else if (type == TYPE_VOICE_INTERACTION) {//音量窗口
         ........
        } else if (type == TYPE_WALLPAPER) {//壁纸窗口
           layoutWallpaper(displayFrames, pf, df, of, cf);
        } else if (win == mStatusBar) {//可以接收输入事件的statusbar窗口
         此窗口的大小一般为全屏的大小(需要看输入法窗口的模式)
        } else {//其他窗口的信息
            dcf.set(displayFrames.mSystem);
            ........
            if (layoutInScreen && layoutInsetDecor) {
                //Window属于“ IN_SCREEN, INSET_DECOR ”
                
                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                            + "): IN_SCREEN, INSET_DECOR");
                // This is the case for a normal activity window: we want it to cover all of the
                // screen space, and it can take care of moving its contents to account for screen
                // decorations that intrude into that space.
                if (attached != null) {
                    // If this window is attached to another, our display
                    // frame is the same as the one we are attached to.
                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
                            displayFrames);
                } else {
                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
                      //type :TYPE_STATUS_BAR_PANEL或者TYPE_STATUS_BAR_SUB_PANEL的设置
                    } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
                          && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
                        // Asking to layout into the overscan region, so give it that pure
                        // unrestricted area.
                        of.set(displayFrames.mOverscan);
                        df.set(displayFrames.mOverscan);
                        pf.set(displayFrames.mOverscan);
                    } else if (canHideNavigationBar()
                            && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                            && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
                            || type == TYPE_VOLUME_OVERLAY)) {
                      .......
                    } else {
                       //com.gwm.settings执行的是这里的代码
                        df.set(displayFrames.mRestrictedOverscan);
                        pf.set(displayFrames.mRestrictedOverscan);
                        // We need to tell the app about where the frame inside the overscan
                        // is, so it can inset its content by that amount -- it didn't ask
                        // to actually extend itself into the overscan region.
                        of.set(displayFrames.mUnrestricted);
                    }     

         .........

        if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
                + ": sim=#" + Integer.toHexString(sim)
                + " attach=" + attached + " type=" + type
                + String.format(" flags=0x%08x", fl)
                + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                + " of=" + of.toShortString()
                + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
                + " dcf=" + dcf.toShortString()
                + " sf=" + sf.toShortString()
                + " osf=" + (osf == null ? "null" : osf.toShortString()));

        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
                parentFrameWasClippedByDisplayCutout);
        //如果是输入法窗口,则需要调整
        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
                && !win.getGivenInsetsPendingLw()) {
            setLastInputMethodWindowLw(null, null);
            offsetInputMethodWindowLw(win, displayFrames);
        }
        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
                && !win.getGivenInsetsPendingLw()) {
            offsetVoiceInputWindowLw(win, displayFrames);
        }
    } 

Log分析

热启动一个应用:com.gwm.settings/.view.ui.MainActivity

//从laucher 点击setting 按钮,

05-11 02:41:11.168 694 1853 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.gwm.settings cmp=com.gwm.settings/.view.ui.MainActivity} from uid 10007

05-11 02:41:11.169 694 1853 I wm_task_moved: [343,0,2147483647]

//将stack:6的设置为当前的focus_stack

05-11 02:41:11.172 694 1853 I am_focused_stack: [0,6,0,bringingFoundTaskToFront]

//AMS:将carlauncher设置为pause状态

05-11 02:41:11.175 694 1853 I am_pause_activity: [0,43469882,com.android.car.carlauncher/.AppGridActivity,userLeaving=true]


//将 task:343 提到最前

05-11 02:41:11.178 694 1853 I am_task_to_front: [0,343]

//添加com.XXX.settings/.view.ui.MainActivity的SnapshotStartingWindow :应用为热启动

05-11 02:41:11.189 694 834 V WindowManager: Adding Window{cabf710 u0 SnapshotStartingWindow for taskId=343} to AppWindowToken{98845b1 token=Token{21da058 ActivityRecord{3f0b13b u0 com.XXX.settings/.view.ui.MainActivity t343}}}

// carlauncher 设置为paused状态,可见,但是没有焦点

05-11 02:41:11.189 2704 2704 I am_on_paused_called: [0,com.android.car.carlauncher.AppGridActivity,performPause]

//将焦点设置为null

05-11 02:41:11.213 694 1853 D WindowManager: Input focus has changed to null


//AMS设置com.gwm.settings/.view.ui.MainActivity为当前resume_activity

05-11 02:41:11.215 694 1853 I am_set_resumed_activity: [0,com.gwm.settings/.view.ui.MainActivity,resumeTopActivityInnerLocked] 05-11 02:41:11.219 694 1853 V WindowManager: notifyAppResumed: wasStopped=true AppWindowToken{98845b1 token=Token{21da058 ActivityRecord{3f0b13b u0 com.gwm.settings/.view.ui.MainActivity t343}}} 05-11 02:41:11.219 694 1853 I am_resume_activity: [0,66105659,343,com.XXX.settings/.view.ui.MainActivity]

//App:com.XXXsettings.view.ui.MainActivity执行on_restart,on_start

05-11 02:41:11.286 19025 19025 I am_on_restart_called: [0,com.XXX.settings.view.ui.MainActivity,performRestartActivity]

05-11 02:41:11.290 19025 19025 I am_on_start_called: [0,com.XXX.settings.view.ui.MainActivity,handleStartActivity]

//执行resume的调用堆栈 com.gwm.settings.view.ui.MainActivity(19025) 05-11 02:41:11.290 19025 19025 V ActivityThread: Performing resume of ActivityRecord{3aa5563 token=android.os.BinderProxy@50a991d {com.gwm.settings/com.gwm.settings.view.ui.MainActivity}} finished=false 05-11 02:41:11.291 19025 19025 D TAG-performResume: Throw 05-11 02:41:11.291 19025 19025 D TAG-performResume: java.lang.Throwable 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.Activity.performResume(Activity.java:7307) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3814) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3854) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:51) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.os.Handler.dispatchMessage(Handler.java:106) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.os.Looper.loop(Looper.java:193) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at android.app.ActivityThread.main(ActivityThread.java:6718) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at java.lang.reflect.Method.invoke(Native Method) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 05-11 02:41:11.291 19025 19025 D TAG-performResume: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 05-11 02:41:11.291 19025 19025 D TAG-performResume: this is :com.gwm.settings.view.ui.MainActivity@9eb0ddb

// app :com.gwm.settings.view.ui.MainActivity执行到on_resume

05-11 02:41:11.291 19025 19025 I am_on_resume_called: [0,com.gwm.settings.view.ui.MainActivity,RESUME_ACTIVITY]

//App:com.gwm.settings.view.ui.MainActivity执行完成resume方法

05-11 02:41:11.293 19025 19025 V ActivityThread: Resuming ActivityRecord{3aa5563 token=android.os.BinderProxy@50a991d {com.gwm.settings/com.gwm.settings.view.ui.MainActivity}} with isForward=true 05-11 02:41:11.293 19025 19025 V ActivityThread: Scheduling idle handler for ActivityRecord{3aa5563 token=android.os.BinderProxy@50a991d {com.gwm.settings/com.gwm.settings.view.ui.MainActivity}}

//这个是wms的relayoutWindow方法中打印的long :com.gwm.settings.view.ui.MainActivity设置的flag如下

05-11 02:41:11.300 694 28400 V WindowManager: Relayout Window{3a93b3d u0 com.gwm.settings/com.gwm.settings.view.ui.MainActivity}: viewVisibility=0 req=1280x768 {(0,0)(fillxfill) sim={adjust=pan forwardNavigation} ty=BASE_APPLICATION wanim=0x10302f8 05-11 02:41:11.300 694 28400 V WindowManager: fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS 05-11 02:41:11.300 694 28400 V WindowManager: pfl=FORCE_DRAW_STATUS_BAR_BACKGROUND}

//performSurfacePlacementInner的调用堆栈: 05-11 02:41:11.300 694 28400 V WindowManager: performSurfacePlacementInner: entry. Called by com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop:207 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:155 com.android.server.wm.WindowManagerService.relayoutWindow:2025 5-11 02:41:11.300 694 28400 W WindowManager: clearLayoutNeeded: callers=com.android.server.wm.DisplayContent.performLayout:2983 com.android.server.wm.DisplayContent.applySurfaceChangesTransaction:2896 com.android.server.wm.RootWindowContainer.applySurfaceChangesTransaction:852 05-11 02:41:11.300 694 28400 V WindowManager: -------------------------------------

//调用到performLayout, dw=1280 dh=768:这个为Dispaly的显示尺寸,不是屏幕的实际尺寸:1860 1080

05-11 02:41:11.300 694 28400 V WindowManager: performLayout: needed=false dw=1280 dh=768 05-11 02:41:11.300 694 28400 V WindowManager: Resolving (mRequestedWidth=1280, mRequestedheight=112) to (pw=1280, ph=112): frame=0,656 ci=0,0 vi=0,0 si=0,0 of=0,0 // beginLayoutLw方法中打印:NavigationBar的frame 05-11 02:41:11.300 694 28400 I WindowManager: mNavigationBar frame: Rect(0, 656 - 1280, 768) //mDock的frame 05-11 02:41:11.300 694 28400 I WindowManager: mDock rect:Rect(0, 0 - 1280, 656)

//layoutStatusBar中的log

05-11 02:41:11.301 694 28400 V WindowManager: Resolving (mRequestedWidth=1280, mRequestedheight=24) to (pw=1280, ph=768): frame=0,0 ci=0,0 vi=0,0 si=0,0 of=0,0 05-11 02:41:11.301 694 28400 V WindowManager: Status bar: dock=Rect(0, 24 - 1280, 656) content=Rect(0, 24 - 1280, 656) cur=Rect(0, 24 - 1280, 656)

//layoutWindowLw中的log,layout的frame为 05-11 02:41:11.303 694 28400 V WindowManager: layoutWindowLw(com.gwm.settings/com.gwm.settings.view.ui.MainActivity): IN_SCREEN, INSET_DECOR 05-11 02:41:11.303 694 28400 V WindowManager: Compute frame com.gwm.settings/com.gwm.settings.view.ui.MainActivity: sim=#120 attach=null type=1 flags=0x81810100 pf=0,0 df=0,0 of=0,0 cf=0,24 vf=0,24 dcf=0,0 sf=0,24 osf=null 05-11 02:41:11.303 694 28400 V WindowManager: Resolving (mRequestedWidth=1280, mRequestedheight=768) to (pw=1280, ph=768): frame=0,0 ci=0,24 vi=0,24 si=0,24 of=0,0 05-11 02:41:11.303 694 28400 V WindowManager: LAYOUT: mFrame=Rect(0, 0 - 1280, 768) mContainingFrame=Rect(0, 0 - 1280, 768) mDisplayFrame=Rect(0, 0 - 1280, 768)

//create surface 05-11 02:41:11.308 694 28400 V WindowManager: Creating surface in session android.view.SurfaceSession@44775df window WindowStateAnimator{1d1522c com.gwm.settings/com.gwm.settings.view.ui.MainActivity} w=1280 h=768 x=0 y=0 format=-1 flags=4

//切换焦点 05-11 02:41:11.310 694 28400 D WindowManager: Input focus has changed to Window{3a93b3d u0 com.gwm.settings/com.gwm.settings.view.ui.MainActivity} 05-11 02:41:11.310 694 28400 V WindowManager: Update reported visibility: AppWindowToken{98845b1 token=Token{21da058 ActivityRecord{3f0b13b u0 com.gwm.settings/.view.ui.MainActivity t343}}}

//relayout 完成 05-11 02:41:11.311 694 28400 V WindowManager: Relayout complete Window{3a93b3d u0 com.gwm.settings/com.gwm.settings.view.ui.MainActivity}: outFrame=0,0

//AMS将carlauncher设置为stop的状态

05-11 02:41:11.782 694 762 I am_stop_activity: [0,43469882,com.android.car.carlauncher/.AppGridActivity]

//APP :com.android.car.carlauncher.AppGridActivity 此时不可见状态

05-11 02:41:11.827 2704 2704 I am_on_stop_called: [0,com.android.car.carlauncher.AppGridActivity,STOP_ACTIVITY_ITEM]

本文地址:https://blog.csdn.net/binghaiwlf/article/details/107387350

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网