一、根本概念
1、窗口表现架构图
- 多窗口的核心原理实在就是分栈和设置栈界限
2、Android的窗口分类
- Android应用步伐窗口,这个是最常见的(拥有自己的WindowToken)譬如:Activity与Dialog
- Android应用步伐子窗口(必须依附到其他非子窗谈锋气存在,通常这个被依附的窗口范例Activity窗口) 例如:PopupWindow
- Android体系窗口,此中我们最最常见的就是Toast窗口了
3、StackId
【Id:0】Home Stack,这个是Launcher地点的Stack。 实在尚有一些体系界面也运行在这个Stack上,例如近期任务
【Id:1】FullScren Stack,全屏的Activity地点的Stack。 但其着实分屏模式下,Id为1的Stack只占了半个屏幕。
【Id:2】Freeform模式的Activity地点Stack
【Id:3】Docked Stack 下文中我们将看到,在分屏模式下,屏幕有一半运行了一个固定的应用,这个就是这里的Docked Stack
【Id:4】Pinned Stack 这个是画中画Activity地点的Stack
4、窗口管理中涉及的几个告急概念
- IWindow: APP端窗口袒露给WMS的抽象实例,同时也是WMS向APP端发送消息的Binder通道,它在APP端的实现为W
- IWindowSession:WMS服务用于提供给ViewRootImpl来和其举行跨Binder通讯的接口
- WindowState:WMS端窗口的令牌,与IWindow窗口逐一对应,是WMS管理窗口的告急依据
- WindowToken:是窗口的令牌,也是窗口分组的依据,在WMS端,和分组对应的数据结构是WindowToken
- Token:是在AMS构建Activity对应的ActivityRecord时内里的IApplicationToken的实例,会在Activity创建过程中通报到AMS中,而且Token会在Activity从创建到表现的过程中会在App进程和AMS,WMS之间举行通报
- ActivityManagerService
Activity管理机制的Binder服务端,属于一个体系服务。用于管理Activity的各种举动,控制Activity的生命周期,派发消息事件,低内存管理等等。实现了IBinder接口,可以用于进程间通讯
- ActivityStarter
用来负责处置惩罚Activity的Intent和Flags, 尚有关联相干的Stack和TaskRecord
- ActivityManagerProxy
AMS服务署理端,第三方应用借助该类实现对AMS的长途RPC请求
- ActivityRecord
顾名思义,该数据结构和我们的Activiyt相对应,存储者Activiyt的相干信息,而且每个ActivityRecord会对应到一个TaskRecord,ActivityRecord中范例为TaskRecord的成员task,纪录所属的Task,这里有一点必要留意的是Activity和ActivityRecord并不是一对一的,而是一对多,由于一个Actitiy大概存在多个启动方式进而导致存在多个ActivityRecord
- TaskRecord
一个TaskRecord由一个大概多个ActivityRecord构成,这就是我们常说的任务栈,具有后进先出的特点
- ActivityStack
用来管理TaskRecord,它有一个ArrayList范例的成员mTaskHistory,用于存储TaskRecord,体系总是表现位于栈顶的Activity
- ActivityDisplay
ActivityDisplay表现一个屏幕,Android支持三种屏幕:主屏幕,外接屏幕(HDMI等),假造屏幕(投屏)。一样平常环境下,即只有主屏幕时,ActivityStackSupervisor与ActivityDisplay都是体系唯一;
ActivityDisplay是ActivityStackSupervisor的内部类,它相称于一个工具类,封装了移除和添加ActivityStack的方法
- ActivityStackSupervisor
负责所有Activity栈的管理。内部管理了mHomeStack、mFocusedStack和mLastFocusedStack三个Activity栈。
- mHomeStack管理的是Launcher相干的Activity栈
- mFocusedStack管理的是当前表现在前台Activity的Activity栈
- mLastFocusedStack管理的是上一次表现在前台Activity的Activity栈
5、AMS和WMS交互图
6、AMS与WMS任务栈对应关系
AMS和WMS在应用窗口这块是有对应关系:
ActivityDisplay----------DisplayContent
ActivityStack------------TaskStack
TaskRecord-------------Task
ActivityRecord----------AppWindowToken
AMS这边依次为:
ActivityDisplay->ActivityStack->TaskRecord->ActivityRecord
WMS依次分为:
DisplayContent(TaskStackContainer)->TaskStack->Task->AppWindowToken
二、窗口添加并盘算
1、Activity窗口尺寸
- ViewRootImpl.setView()开始窗口视图的添加
/** 这里我们分析的是Activity的DecorView窗口视图添加的逻辑,以是此时不存在父视图的概念, 不会走到这里,此时的panelParentView为null */ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { //顶层DecorView mView = view; ....... //添加窗口到WMS,mWindow(Binder范例W,传给WMS以便WMS可以调用应用进程方法) res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets); //mTmpFrame为WMS盘算得到的窗口尺寸 setFrame(mTmpFrame); ...... } } }
- 通过Seesion.java终极调到WindowManagerService.addWindow
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) { int res = mPolicy.checkAddPermission(attrs, appOp); //... synchronized(mWindowMap) { //... final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { Slog.w(TAG, "Attempted to add window to a display that does not exist: " + displayId + ". Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } //... WindowToken token = mTokenMap.get(attrs.token); //... // 新的WindowState对象在其构造函数中根据窗口范例初始化了其主序mBaseLayer和mSubLayer win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); ...... return res;}
- WindowState.WindowState构造初始化
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a, int viewVisibility, final DisplayContent displayContent) { ... //如果是子窗口,使用它依附的窗口范例来盘算 if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { mBaseLayer = mPolicy.windowTypeToLayerLw( attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + WindowManagerService.TYPE_LAYER_OFFSET;//盘算mBaseLayer,关键点1 //表现子窗口和父窗口的相对位置 mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);//盘算mSubLayer,关键点2 ... } else {//非子窗口,直接使用窗口范例来盘算 //注:TYPE_LAYER_MULTIPLIER的值是10000,TYPE_LAYER_OFFSET的值是1000 mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + WindowManagerService.TYPE_LAYER_OFFSET; mSubLayer = 0;//无效,仅在子窗口中有效 ... } ... }
- WindowManagerPolicy.windowTypeToLayerLw
- 盘算主窗口位置
//根据范例返回窗口的种类,从1-31, public int windowTypeToLayerLw(int type) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { return 2; } switch (type) { case TYPE_UNIVERSE_BACKGROUND: return 1; case TYPE_PRIVATE_PRESENTATION: return 2; case TYPE_WALLPAPER: // wallpaper is at the bottom, though the window manager may move it. return 2; case TYPE_PHONE: return 3; case TYPE_SEARCH_BAR: return 4; ... case TYPE_HIDDEN_NAV_CONSUMER: return 30; /// M:JB migration case TYPE_TOP_MOST: return 31; } Log.e(TAG, "Unknown window type: " + type); return 2; }
- WindowManagerPolicy.subWindowTypeToLayerLw
- 盘算子窗口位置
public int subWindowTypeToLayerLw(int type) { switch (type) { case TYPE_APPLICATION_PANEL: case TYPE_APPLICATION_ATTACHED_DIALOG: return APPLICATION_PANEL_SUBLAYER;//便是1 case TYPE_APPLICATION_MEDIA: return APPLICATION_MEDIA_SUBLAYER;//便是-2 case TYPE_APPLICATION_MEDIA_OVERLAY: return APPLICATION_MEDIA_OVERLAY_SUBLAYER;//便是-1 case TYPE_APPLICATION_SUB_PANEL: return APPLICATION_SUB_PANEL_SUBLAYER;//便是2 } Log.e(TAG, "Unknown sub-window type: " + type); return 0; }
- WindowManagerService.getLayoutHintLw: 盘算窗口的巨细
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) { ...... final Rect taskBounds; final boolean floatingStack; if (atoken != null && atoken.getTask() != null) { taskBounds = mTmpRect; //重点:这里getBounds得到的尺寸是在Activity启动阶段调用setBounds设置的 atoken.getTask().getBounds(mTmpRect); floatingStack = atoken.getTask().isFloating(); } else { taskBounds = null; floatingStack = false; } //重点方法getLayoutHintLw if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack, outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; } return res;}6、DisplayPolicy.getLayoutHintLw
public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds, DisplayFrames displayFrames, boolean floatingStack, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { //这里获取和窗口尺寸盘算相干的flag, //如WindowManager.LayoutParams.FLAG_FULLSCREEN,全屏 //WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS等,半透明状态栏 final int fl = PolicyControl.getWindowFlags(null, attrs); final int pfl = attrs.privateFlags; //获取和SystemUI相干 flag, //这些flag界说在View中,大多和是否全屏表现,是否潜伏状态栏,是否潜伏导航栏相干 final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); //屏幕旋转角度 final int displayRotation = displayFrames.mRotation; //是否使用超出真实屏幕的底部像素值 final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); if (useOutsets) { //这个值界说在configs.xml中(config_windowOutsetBottom),默以为0, int outset = mWindowOutsetBottom; if (outset > 0) { if (displayRotation == Surface.ROTATION_0) { outOutsets.bottom += outset; } else if (displayRotation == Surface.ROTATION_90) { outOutsets.right += outset; } else if (displayRotation == Surface.ROTATION_180) { outOutsets.top += outset; } else if (displayRotation == Surface.ROTATION_270) { outOutsets.left += outset; } } } final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; final boolean layoutInScreenAndInsetDecor = layoutInScreen && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; if (layoutInScreenAndInsetDecor && !screenDecor) { if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { //包罗状态栏导航栏的屏幕可见解区 Rect(0, 0 - 800, 480) outFrame.set(displayFrames.mUnrestricted); } else { //除启发航栏的内容地域 Rect(0, 0 - 800, 396) outFrame.set(displayFrames.mRestricted); } final Rect sf; //悬浮栈,窗口模式为自由窗口大概画中画的栈 if (floatingStack) { sf = null; } else { //除开状态栏,导航栏的内容地域 Rect(0, 57 - 800, 396) sf = displayFrames.mStable; } final Rect cf; if (floatingStack) { cf = null; } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if ((fl & FLAG_FULLSCREEN) != 0) { //除启发航栏的内容地域 Rect(0, 0 - 800, 396) cf = displayFrames.mStableFullscreen; } else { //除开状态栏,导航栏的内容地域 Rect(0, 57 - 800, 396) cf = displayFrames.mStable; } } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { //真实屏幕的尺寸 Rect(0, 0 - 800, 480) cf = displayFrames.mOverscan; } else { //除开状态栏,导航栏,输入法的内容地域 Rect(0, 57 - 800, 396) cf = displayFrames.mCurrent; } if (taskBounds != null) { //taskBounds为Activity自己设置的巨细,Rect(400, 57 - 800, 396) outFrame.intersect(taskBounds); } InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets); InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) .getDisplayCutout()); return mForceShowSystemBars; } else { if (layoutInScreen) { //包罗状态栏导航栏的屏幕可见解区 Rect(0, 0 - 800, 480) outFrame.set(displayFrames.mUnrestricted); } else { //除开状态栏,导航栏的内容地域 Rect(0, 57 - 800, 396) outFrame.set(displayFrames.mStable); } if (taskBounds != null) { //taskBounds为Activity自己设置的巨细,Rect(400, 57 - 800, 396) outFrame.intersect(taskBounds); } outContentInsets.setEmpty(); outStableInsets.setEmpty(); outDisplayCutout.set(DisplayCutout.NO_CUTOUT); return mForceShowSystemBars; } }
//DisplayFrames.java//以模仿器尺寸Rect(0, 0 - 800, 480)为例 /** * 真实屏幕的尺寸 Rect(0, 0 - 800, 480) */ public final Rect mOverscan = new Rect(); /** * 除开状态栏,导航栏,输入法的内容地域 Rect(0, 57 - 800, 396) */ public final Rect mCurrent = new Rect();/** * 包罗状态栏导航栏的屏幕可见解区 Rect(0, 0 - 800, 480) */ public final Rect mUnrestricted = new Rect(); /** * 除启发航栏的内容地域 Rect(0, 0 - 800, 396) */ public final Rect mRestricted = new Rect();/** 除开状态栏,导航栏的内容地域 Rect(0, 57 - 800, 396) */ public final Rect mStable = new Rect(); /** * 除启发航栏的内容地域 Rect(0, 0 - 800, 396) */ public final Rect mStableFullscreen = new Rect();2、窗口尺寸盘算
- ViewRootImpl.performTraversals
private void performTraversals() { //DecorView final View host = mView; ...... WindowManager.LayoutParams lp = mWindowAttributes; //这两个变量用来纪录Activity窗口的宽高尺寸 int desiredWindowWidth; int desiredWindowHeight; ...... //mWinFrame用来纪录Activity窗口的尺寸,这个值是WMS盘算的到的 Rect(400, 57 - 800, 396) Rect frame = mWinFrame; //初次进入 if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; final Configuration config = mContext.getResources().getConfiguration(); //是否使用屏幕的尺寸,TYPE_STATUS_BAR_PANEL,TYPE_INPUT_METHOD,TYPE_VOLUME_OVERLAY这三种范例窗口返回true if (shouldUseDisplaySize(lp)) { Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else { //将Activity宽高保存在这两个变量中,w = 400,h = 339 desiredWindowWidth = mWinFrame.width(); desiredWindowHeight = mWinFrame.height(); } ...... //此方法用来处置惩罚大概出现的体系窗口,状态栏,导航栏,输入法,罗升阳老师文章中叫做边衬地域,暂时略过 dispatchApplyInsets(host); } else { desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); //mWidth和mHeight纪录前次WMS为Activity盘算得到的窗口宽高 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { //不等分析窗口尺寸发生了变化 mFullRedrawNeeded = true; mLayoutRequested = true; windowSizeMayChange = true; } } ... //对DecorView举行丈量,宽高使用WMS.addWindow盘算出来的尺寸 windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight); ...... //这里有六个条件,满意此中之一就会对窗口举行再次盘算 if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null || mForceNextWindowRelayout) { ...... //针对窗口发生变化的环境举行再次盘算 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); ...... } ..... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { //缩放系数 便是1 float appScale = mAttachInfo.mApplicationScale; ...... //调用WMS的relayout方法 int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets); if (mSurfaceControl.isValid()) { mSurface.copyFrom(mSurfaceControl); } else { destroySurface(); } ...... //设置WMS再次盘算得到窗口尺寸 setFrame(mTmpFrame); mInsetsController.onStateChanged(mTempInsets); return relayoutResult; }
- 通过Session.java调用到WMS.relayoutWindow
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, SurfaceControl outSurfaceControl, InsetsState outInsetsState) { ...... if (viewVisibility != View.GONE) { //requestedWidth和requestedHeight是Activity窗口颠末丈量后得到的自己的想要的宽高 win.setRequestedSize(requestedWidth, requestedHeight); } ...... //WMS核心功能,革新体系UI,这内里会去盘算窗口尺寸, //遍历体系所有窗口调用其WindowState的computeFrameLw方法 mWindowPlacerLocked.performSurfacePlacement(true /* force */); ...... win.getCompatFrame(outFrame); ...... }
- DisplayPolicy.layoutWindowLw
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { ...... final WindowFrames windowFrames = win.getWindowFrames(); final Rect pf = windowFrames.mParentFrame; final Rect df = windowFrames.mDisplayFrame; final Rect of = windowFrames.mOverscanFrame; final Rect cf = windowFrames.mContentFrame; final Rect vf = windowFrames.mVisibleFrame; final Rect dcf = windowFrames.mDecorFrame; final Rect sf = windowFrames.mStableFrame; ...... //颠末各种条件判断,末了会对上述Rect赋值,赋的值全部来自DisplayFrames中 ..... //有了上述窗口的底子界限之后便开始窗口自己尺寸的盘算了 win.computeFrameLw(); .....}
- WindowState.computeFrameLw : 窗口自己尺寸的盘算
computeFrameLw方法的核心是盘算出mWindowFrames.mFrame的值,这个值就是窗口的实际尺寸
@Override public void computeFrameLw() { if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { return; } mHaveFrame = true; //获取窗口对应的task,非Activity窗口为空 final Task task = getTask(); //是否为全屏,非多窗口模式而且getBounds便是Display的getBounds,我们分析的是分屏窗口,以是这里为false final boolean isFullscreenAndFillsDisplay = !inMultiWindowMode() && matchesDisplayBounds(); //分屏不是悬浮窗口,此处为false final boolean windowsAreFloating = task != null && task.isFloating(); final DisplayContent dc = getDisplayContent(); //这里getBounds返回分屏窗口启动是所设置的界限值,即为Rect(400, 57 - 800, 396) mInsetFrame.set(getBounds()); final Rect layoutContainingFrame; final Rect layoutDisplayFrame; final int layoutXDiff; final int layoutYDiff; //是否有输入法窗口 final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow(); //当前窗口是否为输入法窗口的目的窗口 final boolean isImeTarget = imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget(); if (isFullscreenAndFillsDisplay || layoutInParentFrame()) { //分屏窗口不走这里,省略 ...... } else { //这里的getDisplayedBounds就便是task.getBounds,即为Rect(400, 57 - 800, 396) mWindowFrames.mContainingFrame.set(getDisplayedBounds()); if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) { //冻屏窗口的环境,省略 ...... } // 当前窗口是否为输入法窗口的目的窗口 if (isImeTarget) { //必要盘算输入法的环境,省略 ...... } if (windowsAreFloating) { //悬浮窗口的环境,省略 ...... } //获取窗口地点的栈, final TaskStack stack = getStack(); if (inPinnedWindowingMode() && stack != null && stack.lastAnimatingBoundsWasToFullscreen()) { //画中画模式的窗口,省略 ...... } //mWindowFrames.mDisplayFrame值为 Rect(0, 0 - 800, 480) layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame); //mWindowFrames.mContainingFrame值为 Rect(400, 57 - 800, 396) mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame); //layout得到的尺寸和实际尺寸的偏移量,大多数环境为0 layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left; layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top; layoutContainingFrame = mInsetFrame; //mTmpRect保存屏幕尺寸,为 Rect(0, 0 - 800, 480) mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight); subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame, mTmpRect); //layoutInParentFrame代表当前盘算尺寸的是否为子窗口 if (!layoutInParentFrame()) { subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame, mWindowFrames.mParentFrame, mTmpRect); subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame, mTmpRect); } layoutDisplayFrame.intersect(layoutContainingFrame); } //对于当前窗口为子窗口大概全屏的环境mWindowFrames.mContainingFrame保存的是父窗口的尺寸, //否则mWindowFrames.mContainingFrame保存的就是自己的尺寸 final int pw = mWindowFrames.mContainingFrame.width(); final int ph = mWindowFrames.mContainingFrame.height(); //mRequestedWidth和mRequestedHeight是Activity自己丈量出来的自己的DecorView的宽高 //WMS要团结这个应用自己请求的宽高来盘算窗口的尺寸 if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) { mLastRequestedWidth = mRequestedWidth; mLastRequestedHeight = mRequestedHeight; mWindowFrames.setContentChanged(true); } //mWindowFrames.mFrame保存的是终极窗口盘算出来的实际尺寸,computeFrameLw方法终极要盘算的就是它的值 //现在这里照旧0 final int fw = mWindowFrames.mFrame.width(); final int fh = mWindowFrames.mFrame.height(); //盘算mFrame的核心方法,layoutContainingFrame代表的是父窗口尺寸地域,layoutDisplayFrame代表当前窗口栈地点地域 //大多数环境下这两个值都相称 applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame); // 盘算超出屏幕地域的部门,省略 ..... if (windowsAreFloating && !mWindowFrames.mFrame.isEmpty()) { //悬浮窗口的环境,省略 ...... } else if (mAttrs.type == TYPE_DOCK_DIVIDER) { //窗口范例为TYPE_DOCK_DIVIDER,省略 ...... } else { //mContentFrame的值来自DisplayFrames,为除开状态栏,导航栏的地域 Rect(0, 57 - 800, 396) //这里mContentFrame取值为mContentFrame和mFrame中较小的地域 mWindowFrames.mContentFrame.set( Math.max(mWindowFrames.mContentFrame.left, mWindowFrames.mFrame.left), Math.max(mWindowFrames.mContentFrame.top, mWindowFrames.mFrame.top), Math.min(mWindowFrames.mContentFrame.right, mWindowFrames.mFrame.right), Math.min(mWindowFrames.mContentFrame.bottom, mWindowFrames.mFrame.bottom)); //mVisibleFrame的值来自DisplayFrames,为除开状态栏,导航栏的地域 Rect(0, 57 - 800, 396) //这里mVisibleFrame取值为mVisibleFrame和mFrame中较小的地域 mWindowFrames.mVisibleFrame.set( Math.max(mWindowFrames.mVisibleFrame.left, mWindowFrames.mFrame.left), Math.max(mWindowFrames.mVisibleFrame.top, mWindowFrames.mFrame.top), Math.min(mWindowFrames.mVisibleFrame.right, mWindowFrames.mFrame.right), Math.min(mWindowFrames.mVisibleFrame.bottom, mWindowFrames.mFrame.bottom)); //mStableFrame的值来自DisplayFrames,为除开状态栏,导航栏的地域 Rect(0, 57 - 800, 396) //这里mStableFrame取值为mStableFrame和mFrame中较小的地域 mWindowFrames.mStableFrame.set( Math.max(mWindowFrames.mStableFrame.left, mWindowFrames.mFrame.left), Math.max(mWindowFrames.mStableFrame.top, mWindowFrames.mFrame.top), Math.min(mWindowFrames.mStableFrame.right, mWindowFrames.mFrame.right), Math.min(mWindowFrames.mStableFrame.bottom, mWindowFrames.mFrame.bottom)); //上述三个地域mContentFrame,mVisibleFrame,mStableFrame末了得到的值相称 //mFrame为盘算出来的窗口实际尺寸 } if (isFullscreenAndFillsDisplay && !windowsAreFloating) { //全屏而且非悬浮窗口,省略 ...... } if (mAttrs.type == TYPE_DOCK_DIVIDER) { //范例为TYPE_DOCK_DIVIDER的窗口,省略 ...... } else { //mTmpRect保存了屏幕的尺寸,Rect(0, 0 - 800, 480),将这个尺寸保存到DisplayContent中去 getDisplayContent().getBounds(mTmpRect); mWindowFrames.calculateInsets( windowsAreFloating, isFullscreenAndFillsDisplay, mTmpRect); } ...... //将mFrame保存到mCompatFrame mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); ...... if (mIsWallpaper && (fw != mWindowFrames.mFrame.width() || fh != mWindowFrames.mFrame.height())) { //壁纸窗口 ...... } ...... }
- WindowState.applyGravityAndUpdateFrame : mFrame盘算
窗口的尺寸盘算到此就完成了,终极的结果是保存在mFrame中,末了这个值会返回给APP进程,APP进程ViewRootImpl中调用的relayoutWindow方法重要目的就是请求WMS对窗口举行盘算得到mFrame的值,末了APP将此值保存在了ViewRootImpl的成员变量mWinFrame中。
private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) { // Rect(400, 57 - 800, 396) final int pw = containingFrame.width(); final int ph = containingFrame.height(); final Task task = getTask(); //当前窗口是否占满父容器,对分屏窗口来说inNonFullscreenContainer为true,即不会占满父容器 final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds(); //是否答应当前窗口的巨细无穷制,对分屏窗口来说noLimits为false final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0; //是否占满整个屏幕,对分屏窗口来说fitToDisplay为false final boolean fitToDisplay = (task == null || !inNonFullscreenContainer) || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits); float x, y; int w,h; //是否运行在兼容模式,这里为false final boolean inSizeCompatMode = inSizeCompatMode(); //是否指定窗口缩放系数 if ((mAttrs.flags & FLAG_SCALED) != 0) { //没有指定,省略 ....... } else { if (mAttrs.width == MATCH_PARENT) { //如果分屏窗口宽指定为MATCH_PARENT,则w便是栈宽度 w = pw; } else if (inSizeCompatMode) {//兼容模式 w = (int)(mRequestedWidth * mGlobalScale + .5f); } else { //否则w便是分屏应用请求的宽度 w = mRequestedWidth; } if (mAttrs.height == MATCH_PARENT) { //如果分屏窗口高指定为MATCH_PARENT,则h便是栈高度 h = ph; } else if (inSizeCompatMode) {//兼容模式 h = (int)(mRequestedHeight * mGlobalScale + .5f); } else { //否则h便是分屏应用请求的高度 h = mRequestedHeight; } } if (inSizeCompatMode) { //兼容模式 x = mAttrs.x * mGlobalScale; y = mAttrs.y * mGlobalScale; } else { x = mAttrs.x; y = mAttrs.y; } //非占满父容器,而且非子窗口 if (inNonFullscreenContainer && !layoutInParentFrame()) { //这里是确保窗口的宽高是公道的,对于Activity范例窗口,其最大宽高只能便是地点栈的宽高 w = Math.min(w, pw); h = Math.min(h, ph); } // 给mFrame赋值,这里会思量当前窗口的gravity,x,y的位置,Margin来终极盘算mFrame Gravity.apply(mAttrs.gravity, w, h, containingFrame, (int) (x + mAttrs.horizontalMargin * pw), (int) (y + mAttrs.verticalMargin * ph), mWindowFrames.mFrame); if (fitToDisplay) { //这里是确定盘算出来的窗口尺寸在屏幕地域之内 Gravity.applyDisplay(mAttrs.gravity, displayFrame, mWindowFrames.mFrame); } //给mCompatFrame设置mFrame同样的值 mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); if (inSizeCompatMode) { mWindowFrames.mCompatFrame.scale(mInvGlobalScale); } } |