Android P WMS(5) -- relayoutWindow

Android P WMS(1) -- wms简介

Android P WMS(2) -- wms初始化

Android O WMS(3) -- addwindow

Android P WMS(4) -- removewindow

Android P WMS(5) -- relayoutWindow

Android P WMS(6) -- windowanimator

Android P WMS(7) --wms 问题种类和debug技巧

Android P WMS(8) --View SYstem 简介

Android P WMS(9) --Surface

1.1 参数意思(Link深入理解android 卷3):


session:调用者所在进程的Session实例。
client::要进行 relayout 的窗口。
seq: —个和状态栏/导航栏可见性相关的序列号
attrs:窗口的新布局属性。relayoutWindow()的主要目的就是根据attrs所提供的布局 
参数重新布局一个窗口。客户端可以通过relayoutWindow()函数改变attrs中所定义的 
几乎所有布局属性。但是唯独无法改变窗口类型。
requestedWidth与requestedHeight:客户端所要求的窗口尺寸。在重新布局的过程中, 
WMS会尽贵将窗口的尺寸布局为客户端所要求的大小。
viewVisiblility:窗口的可见性。
flags:定义一些布局行为。
□outFrame :由relayoutWindow()函数返回给调用者的一个Rect类型的实例.它保存了 
窗U被重新布局后的位置与大小
UoutContentlnsets与outVisiblelnsets:这两个参数表示了窗口可以绘制内容的矩形边界 
与可视矩形边界在四个方向上到mFrame的像素差。
outConfiguration:重新布局后,WMS为此窗口计算出的Configuration。
outSurface:用来接收WMS为此窗口分配的Surface。窗口的第一次relayout完成后就 
可以通过它在窗口中进行绘图了。

1.2 relayoutWindow code flow

performTraversals会调用relayoutWindow;其请求WMS来计算相关的窗口大小,创建Surface等等;本章试着分析这个函数,就目前的理解performTraversals主要侧重于客户端对DecorView及其子View的measure,layout,draw等三大流程的处理;而binder call到system_server的WMS,是着重于系统侧对窗口的处理

public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewVisibility, int flags,
        long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
        Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
        DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
        Surface outSurface) {
    int result = 0;
    boolean configChanged;
    
    //权限相关的检查
    final boolean hasStatusBarPermission =
            mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
                    == PackageManager.PERMISSION_GRANTED;
    final boolean hasStatusBarServicePermission =
            mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
                    == PackageManager.PERMISSION_GRANTED;

    long origId = Binder.clearCallingIdentity();
    final int displayId;
    synchronized(mWindowMap) {
        WindowState win = windowForClientLocked(session, client, false);  //获取重新布局的window对象
        if (win == null) {
            return 0;
        }
        displayId = win.getDisplayId();

        WindowStateAnimator winAnimator = win.mWinAnimator;
        if (viewVisibility != View.GONE) {
            win.setRequestedSize(requestedWidth, requestedHeight);  //把请求window的width和height保存到window
        }

        win.setFrameNumber(frameNumber);
        int attrChanges = 0;
        int flagChanges = 0;
        if (attrs != null) {
            mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);    //调整窗口参数
            // if they don't have the permission, mask out the status bar bits
            ...
            if (win.mAttrs.type != attrs.type) {
                throw new IllegalArgumentException(
                        "Window type can not be changed after the window is added.");
            }

            // Odd choice but less odd than embedding in copyFrom()
            if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
                    != 0) {
                attrs.x = win.mAttrs.x;
                attrs.y = win.mAttrs.y;
                attrs.width = win.mAttrs.width;
                attrs.height = win.mAttrs.height;
            }

            flagChanges = win.mAttrs.flags ^= attrs.flags;
            attrChanges = win.mAttrs.copyFrom(attrs);    //copyfrom获取变化的属性
            if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                    | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
                win.mLayoutNeeded = true;
            }
            ...
        }

        if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
        ...

        final int oldVisibility = win.mViewVisibility;

        // If the window is becoming visible, visibleOrAdding may change which may in turn
        // change the IME target.
        
        final boolean becameVisible =
                (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                        && viewVisibility == View.VISIBLE;
        //判断是否需要输入法到window
        boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                || becameVisible;
        final boolean isDefaultDisplay = win.isDefaultDisplay();
        boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility     //判断焦点是否改变
                || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                || (!win.mRelayoutCalled));

        boolean wallpaperMayMove = win.mViewVisibility != viewVisibility    //判断是否需要移动壁纸
                && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
        wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
        if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
            winAnimator.mSurfaceController.setSecure(isSecureLocked(win));
        }
        ...
        // This must be called before the call to performSurfacePlacement.
        if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
            if (DEBUG_VISIBILITY) {
                Slog.i(TAG_WM,
                        "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit);
            }
            result |= RELAYOUT_RES_SURFACE_CHANGED;
            if (!win.mWillReplaceWindow) {
                focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay,
                        focusMayChange);
            }
        }

        // We may be deferring layout passes at the moment, but since the client is interested
        // in the new out values right now we need to force a layout.
        //核心方法  WMS的窗口计算、通知绘制
        mWindowPlacerLocked.performSurfacePlacement(true /* force */);    //api  23以前的核心方法——performLayoutAndPlaceSurfacesLockedInner

        if (shouldRelayout) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");

            result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);   //销毁窗口surface

            try {
                result = createSurfaceControl(outSurface, result, win, winAnimator);
            } catch (Exception e) {
                mInputMonitor.updateInputWindowsLw(true /*force*/);

                Slog.w(TAG_WM, "Exception thrown when creating surface for client "
                         + client + " (" + win.mAttrs.getTitle() + ")",
                         e);
                Binder.restoreCallingIdentity(origId);
                return 0;
            }
            if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                focusMayChange = isDefaultDisplay;
            }
            if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
                setInputMethodWindowLocked(win);
                imMayMove = true;
            }
            win.adjustStartingWindowFlags();
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        } else {
           ...
        }

        if (focusMayChange) {
            //System.out.println("Focus may change: " + win.mAttrs.getTitle());
            if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,   //focus改变更新window
                    false /*updateInputWindows*/)) {
                imMayMove = false;
            }
            //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
        }

        // updateFocusedWindowLocked() already assigned layers so we only need to
        // reassign them at this point if the IM window state gets shuffled
        boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
        final DisplayContent dc = win.getDisplayContent();
        if (imMayMove) {
            dc.computeImeTarget(true /* updateImeTarget */);            //输入法移动的话,重新计算ime位置
            if (toBeDisplayed) {
                // Little hack here -- we -should- be able to rely on the function to return
                // true if the IME has moved and needs its layer recomputed. However, if the IME
                // was hidden and isn't actually moved in the list, its layer may be out of data
                // so we make sure to recompute it.
                dc.assignWindowLayers(false /* setLayoutNeeded */);    //重新计算layer
            }
        }

        if (wallpaperMayMove) {
            win.getDisplayContent().pendingLayoutChanges |=
                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
        }

        if (win.mAppToken != null) {
            mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
        }

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                "relayoutWindow: updateOrientationFromAppTokens");
        configChanged = updateOrientationFromAppTokensLocked(displayId);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

        if (toBeDisplayed && win.mIsWallpaper) {
            DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
            dc.mWallpaperController.updateWallpaperOffset(
                    win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);   //壁纸移动,更新壁纸
        }
        ...
        
        //把布局结果给调用者
        outFrame.set(win.mCompatFrame);   //Outframe  最终屏幕的位置和大小
        outOverscanInsets.set(win.mOverscanInsets);
        outContentInsets.set(win.mContentInsets);
        win.mLastRelayoutContentInsets.set(win.mContentInsets);
        outVisibleInsets.set(win.mVisibleInsets);
        outStableInsets.set(win.mStableInsets);
        outCutout.set(win.mDisplayCutout.getDisplayCutout());
        outOutsets.set(win.mOutsets);
        outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
      ...
    }

    if (configChanged) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: sendNewConfiguration");
        sendNewConfiguration(displayId);     //通知ams更新配置,  打印evwntlog configuration_changed
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    Binder.restoreCallingIdentity(origId);
    return result;
}

 

1.3  performSurfacePlacement

计算window的大小、执行动画、更新surface等。而这些关键工作在android api版本23以前(包括api 23)是由performLayoutAndPlaceSurfacesLockedInner这个方法实现的。WindowSurfacePlacer的出现原因可能是因为WMS的performLayoutAndPlaceSurfacesLockedInner方法过于臃肿影响可读性(其实WindowSurfacePlacer里也没好到哪儿去),更重要的原因为了实现7.0中的多窗口功能。

@/frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
    if (mDeferDepth > 0 && !force) {
        return;
    }
    int loopCount = 6;
    do {
        mTraversalScheduled = false;
        performSurfacePlacementLoop();
        mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
        loopCount--;
    } while (mTraversalScheduled && loopCount > 0);
    mService.mRoot.mWallpaperActionPending = false;
}

private void performSurfacePlacementLoop() {
    ...
    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
    mInLayout = true;

    boolean recoveringMemory = false;
    if (!mService.mForceRemoves.isEmpty()) {
        recoveringMemory = true;
        // Wait a little bit for things to settle down, and off we go.
        while (!mService.mForceRemoves.isEmpty()) {
            final WindowState ws = mService.mForceRemoves.remove(0);
            Slog.i(TAG, "Force removing: " + ws);
            ws.removeImmediately();
        }
        Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
        Object tmp = new Object();
        synchronized (tmp) {
            try {
                tmp.wait(250);
            } catch (InterruptedException e) {
            }
        }
    }

    try {
        mService.mRoot.performSurfacePlacement(recoveringMemory);  //here

        mInLayout = false;

        ...
    } ...
}
    
@/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement(boolean recoveringMemory) {
    if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
            + Debug.getCallers(3));

    int i;
    ...
    if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
            ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
    mService.openSurfaceTransaction();
    try {
        applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);  //here
    } catch (RuntimeException e) {
        Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    } finally {
        mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
    }
...

    mService.scheduleAnimationLocked();  //2 开始动画
}

通过在phonewindowManager layoutWindowLw 添加callback看,流程如下

Session#relayout --> Wms#relayoutWindow --> WindowSurfacePlacer.performSurfacePlacement --> PerformSurfacePlacementLoop --> RootWindowContainer.performSurfacePlacement
 --> ApplySurfacechangesTransaction --> DisplayContent.applySurfaceChangesTransaction --> DisplayContent.performlayout -->  ForAllWindow --> apply -->  PhoneWindowManager.layoutWindowLw
     
Session.remove   -->    Wms.removeWindow  --> WindowState.removeifpossible  -->    SetupWindowForRemoveOnExit   --> WindowSurfacePlacer.performSurfacePlacement --> PerformSurfacePlacementLoop --> RootWindowContainer.performSurfacePlacement
 --> ApplySurfacechangesTransaction --> DisplayContent.applySurfaceChangesTransaction --> DisplayContent.performlayout -->  ForAllWindow --> apply -->  PhoneWindowManager.layoutWindowLw

1.4  DisplayContent#performLayout(执行布局)

DisplayContent#performLayout
准备阶段:调用PhoneWindowManager类的成员函数beginLayoutLw来设置屏幕的大小;包括状态栏,导航栏
计算阶段:调用forAllWindows分别计算父窗口和子窗口的大小。(如上文callback)
结束阶段:调用PhoneWindowManager类的成员函数finishLayoutLw来执行一些清理工作

#performLayout

@/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
    void performLayout(boolean initial, boolean updateInputWindows) {
        if (!isLayoutNeeded()) {  //第一次performLayout  initial = true
            return;
        }
        clearLayoutNeeded();

        final int dw = mDisplayInfo.logicalWidth;
        final int dh = mDisplayInfo.logicalHeight;
        if (DEBUG_LAYOUT) {
            Slog.v(TAG, "-------------------------------------");
            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
        }

        mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
        // TODO: Not sure if we really need to set the rotation here since we are updating from the
        // display info above...
        mDisplayFrames.mRotation = mRotation;
        mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);    //1 设置屏幕的大小;包括状态栏,导航栏
        if (isDefaultDisplay) { //mDisplayFrames维护整个屏幕的大小属性
            // 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;
        mLayoutSeq = seq;

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

        // First perform layout of any root windows (not attached to another window).
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);  //2 计算所有父窗口的大小

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

        // Now perform layout of attached windows, which usually depend on the position of the
        // window they are attached to. XXX does not deal with windows that are attached to windows
        // that are themselves attached.
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);

        // Window frames may have changed. Tell the input dispatcher about it.
        mService.mInputMonitor.layoutInputConsumers(dw, dh);
        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
        if (updateInputWindows) {
            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
        }

        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
    }

 

1.4.1 PhoneWindowManager#beginLayoutLw

    @Override
    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
        displayFrames.onBeginLayout();    //初始化DisplayFrames的属性值
        // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
        mDockLayer = 0x10000000;
        mStatusBarLayer = -1;
        dcf.setEmpty();  // Decor frame N/A for system bars.

        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
          ...

            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
                    navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
            if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
            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);
        }
    }

1.4.2 PhoneWindowManager#beginLayoutLw

计算窗口大小是在layoutWindowLw中完成的,该方法不会直接计算出窗口大小,而是先计算出窗口能够扩展的最大空间,然后再调用WindowState的computeFrameLw来计算出窗口的最终大小(很长,针对各种类型窗口的处理)

参考: WMS相关学习-relayoutWindow(6)

    /** {@inheritDoc} */
    @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.

 

参考:

WMS相关学习-relayoutWindow(6)

Android7.0窗口动画设置流程

WMS相关学习-relayoutWindow(5)

WMS:窗口大小的计算

深入理解Android 卷3

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值