wms服务总结没有梳理好,因为关联的模块实在太多,最后只能先把整体框架梳理清楚,根据需要准备把activity窗口位置,大小计算流程详细分析下。本文基于Android O代码分析。
activity只是界面框架,真正控制视图的是view, 先总结下view整体绘制流程,计算窗口的位置,大小只是绘制流程中的一小部分流程,总分总的思路分析流程。
一、 View 的测量、布局、绘制流程
下图是ViewRootImpl里面整体绘制流程
第1步,这里简单理解系统从ViewRootImpl#performTraversals来开始绘制,Choreographer封装VSYNC信号处理,并回调doTraversal->performTraversals,Choreographer特性参考
performTraversals整体主要做4件事情:
1. performMeasure:用来测量View大小
2. relayoutWindow:用来测量Window大小,这里面会创建surface, 计算window位置,大小。
3. performLayout:用来对View布局
4. performDraw:处理View的绘制
//ViewRootImpl.java,
1578 private void performTraversals() {
1579 // cache mView since it is used so much below...
1580 final View host = mView;
1581
1582 if (host == null || !mAdded)
1583 return;
1584
1585 mIsInTraversal = true;
1586 mWillDrawSoon = true;
1587 boolean windowSizeMayChange = false;
1588 boolean newSurface = false;
1589 boolean surfaceChanged = false;
1590 WindowManager.LayoutParams lp = mWindowAttributes;
1592 int desiredWindowWidth; //view期望的宽,高
1593 int desiredWindowHeight;
1594
1595 //...
1596
1597 mWindowAttributesChangesFlag = 0;
1598
1599 Rect frame = mWinFrame; // 保存wms返回计算的window的位置,大小
1600 if (mFirst) { // 当前view第一次执行performTraversals
1601 mFullRedrawNeeded = true;
1602 mLayoutRequested = true;
1603
1604 final Configuration config = mContext.getResources().getConfiguration();
1605 if (shouldUseDisplaySize(lp)) {
1606 // NOTE -- system code, won't try to do compat mode.
1607 Point size = new Point();
1608 mDisplay.getRealSize(size);
1609 desiredWindowWidth = size.x;
1610 desiredWindowHeight = size.y;
1611 } else {
1612 desiredWindowWidth = dipToPx(config.screenWidthDp);
1613 desiredWindowHeight = dipToPx(config.screenHeightDp);
1614 }
1615 //...
1616 host.dispatchAttachedToWindow(mAttachInfo, 0); //向子类分发attachWindow 事件,只有第一次的时候才会。这里其实是很重要的把mAttachInfo 传进去了
1617 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
1618 dispatchApplyInsets(host);
1619 //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
1620
1621 } else { //判断大小是否改变
1622 desiredWindowWidth = frame.width();
1623 desiredWindowHeight = frame.height();
1624 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
1625 if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
1626 mFullRedrawNeeded = true;
1627 mLayoutRequested = true;
1628 windowSizeMayChange = true;
1629 }
1630 }
1631
1632 if (viewVisibilityChanged) { //判断可见性是否改变
1633 mAttachInfo.mWindowVisibility = viewVisibility;
1634 host.dispatchWindowVisibilityChanged(viewVisibility);
1635 if (viewUserVisibilityChanged) {
1636 host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
1637 }
1638 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
1639 endDragResizing();
1640 destroyHardwareResources();
1641 }
1642 if (viewVisibility == View.GONE) {
1643 // After making a window gone, we will count it as being
1644 // shown for the first time the next time it gets focus.
1645 mHasHadWindowFocus = false;
1646 }
1647 }
1648
1649 // Non-visible windows can't hold accessibility focus.
1650 if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
1651 host.clearAccessibilityFocus();
1652 }
1653
1654 // Execute enqueued actions on every traversal in case a detached view enqueued an action
1655 getRunQueue().executeActions(mAttachInfo.mHandler);
1656
1657 boolean insetsChanged = false; // 边衬区是否改变
1658
1659 boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
1660 if (layoutRequested) {
1661
1662 final Resources res = mView.getContext().getResources();
1663
1664 if (mFirst) {
1665 // make sure touch mode code executes by setting cached value
1666 // to opposite of the added touch mode.
1667 mAttachInfo.mInTouchMode = !mAddedTouchMode;
1668 ensureTouchModeLocally(mAddedTouchMode);
1669 } else { //判断边衬去是否改变
1670 if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
1671 insetsChanged = true;
1672 }
1673 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
1674 insetsChanged = true;
1675 }
1676 if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
1677 insetsChanged = true;
1678 }
1679 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
1680 mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
1681 if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
1682 + mAttachInfo.mVisibleInsets);
1683 }
1684 //...
1685 }
1686
1687 // Ask host how big it wants to be
1688 // step1, 初步对view测量,计算预期大小
1689 windowSizeMayChange |= measureHierarchy(host, lp, res,
1690 desiredWindowWidth, desiredWindowHeight);
1691 }
1692
1693 if (collectViewAttributes()) {
1694 params = lp;
1695 }
1696 if (mAttachInfo.mForceReportNewAttributes) {
1697 mAttachInfo.mForceReportNewAttributes = false;
1698 params = lp;
1699 }
1700
1701 if (mFirst || mAttachInfo.mViewVisibilityChanged) { //第一次或者可见性变化
1702 mAttachInfo.mViewVisibilityChanged = false;
1703 int resizeMode = mSoftInputMode &
1704 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
1705 // If we are in auto resize mode, then we need to determine
1706 // what mode to use now.
1707 if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
1708 final int N = mAttachInfo.mScrollContainers.size();
1709 //...
1710 }
1711 }
1712 if (layoutRequested) {
1713 // Clear this now, so that if anything requests a layout in the
1714 // rest of this function we will catch it and re-run a full
1715 // layout pass.
1716 mLayoutRequested = false;
1717 }
1718
1719 boolean windowShouldResize = layoutRequested && windowSizeMayChange
1720 && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
1721 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
1722 frame.width() < desiredWindowWidth && frame.width() != mWidth)
1723 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
1724 frame.height() < desiredWindowHeight && frame.height() != mHeight));
1725 windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
1726
1727 // If the activity was just relaunched, it might have unfrozen the task bounds (while
1728 // relaunching), so we need to force a call into window manager to pick up the latest
1729 // bounds.
1730 windowShouldResize |= mActivityRelaunched;
1731
1732 // Determine whether to compute insets.
1733 // If there are no inset listeners remaining then we may still need to compute
1734 // insets in case the old insets were non-empty and must be reset.
1735 final boolean computesInternalInsets =
1736 mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
1737 || mAttachInfo.mHasNonEmptyGivenInternalInsets;
1738
1739 boolean insetsPending = false;
1740 int relayoutResult = 0;
1741 boolean updatedConfiguration = false;
1742
1743 final int surfaceGenerationId = mSurface.getGenerationId();
1744
1745 final boolean isViewVisible = viewVisibility == View.VISIBLE;
1746 final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
1747 //第一次进来、window 需要重新测量、边衬区改变、view可见性改变、params 不为null 体现的是Window 的属性变化等、强制刷新
1748 if (mFirst || windowShouldResize || insetsChanged ||
1749 viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
1750 mForceNextWindowRelayout = false;
1751
1752 //...
1753 try {
1754 if (DEBUG_LAYOUT) {
1755 Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
1756 host.getMeasuredHeight() + ", params=" + params);
1757 }
1758 //TODO: step2, 向wms申请surface, 并计算window位置,大小
1759 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
1760
1761 if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
1762 + " overscan=" + mPendingOverscanInsets.toShortString()
1763 + " content=" + mPendingContentInsets.toShortString()
1764 + " visible=" + mPendingVisibleInsets.toShortString()
1765 + " visible=" + mPendingStableInsets.toShortString()
1766 + " outsets=" + mPendingOutsets.toShortString()
1767 + " surface=" + mSurface);
1768
1769 final Configuration pendingMergedConfig =
1770 mPendingMergedConfiguration.getMergedConfiguration();
1771 if (pendingMergedConfig.seq != 0) {
1772 if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
1773 + pendingMergedConfig);
1774 performConfigurationChange(mPendingMergedConfiguration, !mFirst,
1775 INVALID_DISPLAY /* same display */);
1776 pendingMergedConfig.seq = 0;
1777 updatedConfiguration = true;
1778 }
1779
1780 // 处理衬边区域的改变
1781 final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
1782 mAttachInfo.mOverscanInsets);
1783 contentInsetsChanged = !mPendingContentInsets.equals(
1784 mAttachInfo.mContentInsets);
1785 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
1786 mAttachInfo.mVisibleInsets);
1787 final boolean stableInsetsChanged = !mPendingStableInsets.equals(
1788 mAttachInfo.mStableInsets);
1789 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
1790 final boolean surfaceSizeChanged = (relayoutResult
1791 & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
1792 final boolean alwaysConsumeNavBarChanged =
1793 mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
1794 //...
1795
1796 if (!hadSurface) { //处理surface,申请buffer
1797 if (mSurface.isValid()) {
1798 }
1799 }
1800 } else if (!mSurface.isValid()) {
1801 // If the surface has been removed, then reset the scroll
1802 }
1803
1804 mAttachInfo.mWindowLeft = frame.left;
1805 mAttachInfo.mWindowTop = frame.top;
1806
1807 if (mSurfaceHolder != null) { //已经拥有了Surface,准备开始绘制
1808 // The app owns the surface; tell it about what is going on.
1809 if (mSurface.isValid()) {
1810 // XXX .copyFrom() doesn't work!
1811 //mSurfaceHolder.mSurface.copyFrom(mSurface);
1812 mSurfaceHolder.mSurface = mSurface;
1813 }
1814 }
1815
1816 if (!mStopped || mReportNextDraw) {
1817 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
1818 (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
1819 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
1820 || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
1821 updatedConfiguration) { // focus 改变、大小发生变化、边衬区发生变化、更新配置
1822 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
1823 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
1824
1825 if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
1826 + mWidth + " measuredWidth=" + host.getMeasuredWidth()
1827 + " mHeight=" + mHeight
1828 + " measuredHeight=" + host.getMeasuredHeight()
1829 + " coveredInsetsChanged=" + contentInsetsChanged);
1830
1831 // Ask host how big it wants to be
1832 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
1833
1834 }
1835 } else {
1836 }
1837
1838 final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
1839 boolean triggerGlobalLayoutListener = didLayout
1840 || mAttachInfo.mRecomputeGlobalAttributes;
1841 if (didLayout) { //step3. 开始处理view布局
1842 performLayout(lp, mWidth, mHeight);
1843
1844 // By this point all views have been sized and positioned
1845 // We can compute the transparent area
1846
1847 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
1848 // start out transparent
1849 // TODO: AVOID THAT CALL BY CACHING THE RESULT?
1850 host.getLocationInWindow(mTmpLocation);
1851 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
1852 mTmpLocation[0] + host.mRight - host.mLeft,
1853 mTmpLocation[1] + host.mBottom - host.mTop);
1854
1855 host.gatherTransparentRegion(mTransparentRegion);
1856 if (mTranslator != null) {
1857 mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
1858 }
1859
1860 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
1861 mPreviousTransparentRegion.set(mTransparentRegion);
1862 mFullRedrawNeeded = true;
1863 // reconfigure window manager
1864 try {
1865 mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
1866 } catch (RemoteException e) {
1867 }
1868 }
1869 }
1870
1871 }
1872
1873 if (triggerGlobalLayoutListener) {
1874 mAttachInfo.mRecomputeGlobalAttributes = false;
1875 mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
1876 }
1877
1878 if (computesInternalInsets) {
1879 // Clear the original insets.
1880 final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
1881 insets.reset();
1882 }
1883
1884 if (mFirst && sAlwaysAssignFocus) { // 第一次获取焦点
1885 // handle first focus request
1886 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
1887 + mView.hasFocus());
1888 if (mView != null) {
1889 if (!mView.hasFocus()) {
1890 mView.restoreDefaultFocus();
1891 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
1892 + mView.findFocus());
1893 } else {
1894 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
1895 + mView.findFocus());
1896 }
1897 }
1898 }
1899
1900 final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
1901 final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
1902 final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
1903 if (regainedFocus) {
1904 mLostWindowFocus = false;
1905 } else if (!hasWindowFocus && mHadWindowFocus) {
1906 mLostWindowFocus = true;
1907 }
1908
1909 if (changedVisibility || regainedFocus) {
1910 // Toasts are presented as notifications - don't present them as windows as well
1911 boolean isToast = (mWindowAttributes == null) ? false
1912 : (mWindowAttributes.type == WindowManager.LayoutParams.TYPE_TOAST);
1913 if (!isToast) {
1914 host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1915 }
1916 }
1917
1918 mFirst = false;
1919 mWillDrawSoon = false;
1920 mNewSurfaceNeeded = false;
1921 mActivityRelaunched = false;
1922 mViewVisibility = viewVisibility;
1923 mHadWindowFocus = hasWindowFocus;
1924
1925 if (hasWindowFocus && !isInLocalFocusMode()) {
1926 final boolean imTarget = WindowManager.LayoutParams
1927 .mayUseInputMethod(mWindowAttributes.flags);
1928 if (imTarget != mLastWasImTarget) {
1929 mLastWasImTarget = imTarget;
1930 InputMethodManager imm = InputMethodManager.peekInstance();
1931 if (imm != null && imTarget) {
1932 imm.onPreWindowFocus(mView, hasWindowFocus);
1933 imm.onPostWindowFocus(mView, mView.findFocus(),
1934 mWindowAttributes.softInputMode,
1935 !mHasHadWindowFocus, mWindowAttributes.flags);
1936 }
1937 }
1938 }
1939
1940 // Remember if we must report the next draw.
1941 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
1942 reportNextDraw();
1943 }
1944
1945 boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
1946
1947 if (!cancelDraw && !newSurface) {
1948 if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
1949 for (int i = 0; i < mPendingTransitions.size(); ++i) {
1950 mPendingTransitions.get(i).startChangingAnimations();
1951 }
1952 mPendingTransitions.clear();
1953 }
1954
1955 //step4: 开始绘制view
1956 performDraw();
1957 } else {
1958 if (isViewVisible) {
1959 // Try again
1960 scheduleTraversals();
1961 } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
1962 for (int i = 0; i < mPendingTransitions.size(); ++i) {
1963 mPendingTransitions.get(i).endChangingAnimations();
1964 }
1965 mPendingTransitions.clear();
1966 }
1967 }
1968
1969 mIsInTraversal = false;
1970 }
第1689行开始测量View,第83行是测量完Window以后发现大小变化以后重新测量View。
第1759行测量Window。
第1842行开始对View布局。
第1956行开始绘制View。
这里先了解下View的测量(measure)、布局(layout)和绘制(draw)的整体流程,下面还是重点分析下relayoutWindow流程。
二、Android窗口位置,大小计算基础知识整理
参考老罗的博客,Activity窗口的大小等于整个屏幕的大小,但是它并不占据着整块屏幕。为了理解这一点,我们首先分析一下Activity窗口的区域是如何划分的。
content region和content insets区域
Activity窗口的上方一般会有一个状态栏,用来显示4G信号、电量使用等图标,如下图:
从Activity窗口剔除掉状态栏所占用的区域之后,所得到的区域就称为内容区域(Content Region)。顾名思义,内容区域就是用来显示Activity窗口的内容的。我们再抽象一下,假设Activity窗口的四周都有一块类似状态栏的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为内容区域,而被剔除出来的区域所组成的区域就称为内容边衬区域(Content Insets)。Activity窗口的内容边衬区域可以用一个四元组(content-left, content-top, content-right, content-bottom)来描述,其中,content-left、content-right、content-top、content-bottom分别用来描述内容区域与窗口区域的左右上下边界距离。
visible region和visible insets区域
Activity窗口有时候需要显示输入法窗口, 如下图:
这时候Activity窗口的内容区域的大小有可能没有发生变化,这取决于它的Soft Input Mode。我们假设Activity窗口的内容区域没有发生变化,但是它在底部的一些区域被输入法窗口遮挡了,即它在底部的一些内容是不可见的。从Activity窗口剔除掉状态栏和输入法窗口所占用的区域之后,所得到的区域就称为可见区域(Visible Region)。同样,我们再抽象一下,假设Activity窗口的四周都有一块类似状态栏和输入法窗口的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为可见区域,而被剔除出来的区域所组成的区域就称为可见边衬区域(Visible Insets)。Activity窗口的可见边衬区域可以用一个四元组(visible-left, visible-top, visible-right, visible-bottom)来描述,其中,visible-left、visible-right、visible-top、visible-bottom分别用来描述可见区域与窗口区域的左右上下边界距离。
在大多数情况下,Activity窗口的内容区域和可见区域的大小是一致的,而状态栏和输入法窗口所占用的区域又称为屏幕装饰区。理解了这些概念之后,我们就可以推断,WindowManagerService服务实际上就是需要根据屏幕以及可能出现的状态栏和输入法窗口的大小来计算出Activity窗口的整体大小及其内容区域边衬和可见区域边衬的大小。有了这三个数据之后,Activity窗口就可以对它里面的UI元素进行测量、布局以及绘制等操作了。
ViewRootImpl中与窗口大小相关的变量
ViewRootImpl中很多窗口相关的变量都是通过wms计算后返回,ViewRootImpl进一步给View布局使用。
Member | Description |
---|---|
int mWidth; int mHeight; |
当前真实展示宽高 |
Rect mWinFrame; | WSM提供,当Activity窗口大小改变时,WMS通过W.resized接口通知客户端mWinFrame就用于记录WMS提供的宽高 |
Rect mPendingVisibleInsets | 假设一个Window 的四周都有被一块类似输入框的区域遮住(注意是遮住,那块区域还是存在),得到中间的那一块区域就称为可见区域,而被剔除出来的区域所组成的区域就称为可见边衬区域(Visible Insets) |
Rect mPendingContentInsets | 假设一个Window 的四周都有一块类似状态栏的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为内容区域,而被剔除出来的区域所组成的区域就称为内容边衬区域(Content Insets)(来自老罗博客,最下面有地址) |
Rect mPendingStableInsets | 代表的是剔除System Bar 所占据的位置以后的区域(不管Status Bar 和Navigation Bar 是否可见都会计算在内) |
WindowState中成员变量的含义
WindowState是WMS 用来标识一个Window的,wms中保存最终计算好的位置,大小信息。
Member | Description |
---|---|
mRequestedWidth mRequestedHeight mLastRequestedWidth mLastRequestedHeight |
The window size that was requested by the application 应用请求Window 的大小 |
mVisibleInsets mLastVisibleInsets mVisibleInsetsChanged |
Insets that determine the actually visible area Window 的可见边衬区 |
mContentInsets mLastContentInsets mContentInsetsChanged |
Insets that are covered by system windows (such as the status bar) and transient docking windows (such as the IME) Window 的内容边衬区 |
mOverscanInsets mLastOverscanInsets mOverscanInsetsChanged |
Insets that determine the area covered by the display overscan region. Window 的过扫描边衬区 |
mStableInsets mLastStableInsets mStableInsetsChanged |
Insets that determine the area covered by the stable system windows Window 被系统Window 遮住的固定边衬区 |
mOutsets mLastOutsets mOutsetsChanged |
Outsets determine the area outside of the surface where we want to pretend that it's possible to draw anyway 不在Window 的Surface 区域但可以绘制的区域 |
mFrame mLastFrame mFrameSizeChanged |
"Real" frame that the application sees, in display coordinate space 最后展示的区域 |
PhoneWindowManager中成员变量的含义
Member | Description |
---|---|
mOverscanScreenLeft mOverscanScreenTop mOverscanScreenWidth mOverscanScreenHeight |
The current size of the screen; really; extends into the overscan area of the screen and doesn't account for any system elements like the status bar 屏幕的真实坐标,包含了过扫描的区域。代表的是左上角坐标和屏幕宽高 |
mUnrestrictedScreenLeft mUnrestrictedScreenTop mUnrestrictedScreenWidth mUnrestrictedScreenHeight |
The current visible size of the screen; really; (ir)regardless of whether the status bar can be hidden but not extending into the overscan area 不包含过扫描区域,包含了状态栏的区域的大小 |
mRestrictedOverscanScreenLeft mRestrictedOverscanScreenTop mRestrictedOverscanScreenWidth mRestrictedOverscanScreenHeight |
Like mOverscanScreen, but allowed to move into the overscan region where appropriate 与mOverscanScreen 类似,但是可以移动到过扫描区域 |
mRestrictedScreenLeft mRestrictedScreenTop mRestrictedScreenWidth mRestrictedScreenHeight |
The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar can't be hidden; in that case it effectively carves out that area of the display from all other windows 屏幕当前的区域,但是不包含状态栏 |
mSystemLeft mSystemTop mSystemRight mSystemBottom |
During layout, the current screen borders accounting for any currently visible system UI elements. 所有可见的UI元素区域(包含了系统状态栏区域) |
mStableLeft mStableTop mStableRight mStableBottom |
For applications requesting stable content insets, these are them. 不包含状态栏的区域(不管状态栏是否可见) |
mStableFullscreenLeft mStableFullscreenTop mStableFullscreenRight mStableFullscreenBottom |
For applications requesting stable content insets but have also set the fullscreen window flag, these are the stable dimensions without the status bar. 与mStable*类似。不包含状态栏的区域 |
mCurLeft mCurTop mCurRight mCurBottom |
During layout, the current screen borders with all outer decoration (status bar, input method dock) accounted for. 包含状态栏、输入法的内容区域 |
mContentLeft mContentTop mContentRight mContentBottom |
During layout, the frame in which content should be displayed to the user, accounting for all screen decoration except for any space they deem as available for other content. This is usually the same as mCur*, but may be larger if the screen decor has supplied content insets 内容区域 |
mVoiceContentLeft mVoiceContentTop mVoiceContentRight mVoiceContentBottom |
During layout, the frame in which voice content should be displayed to the user, accounting for all screen decoration except for any space they deem as available for other content. 语音区域 |
mDockLeft mDockTop mDockRight mDockBottom |
During layout, the current screen borders along which input method windows are placed 输入法区域 |
PhoneWindowManager中还有一系列关于WindowState计算的值。下面的9个值都是Rect,代表的都是左上角和右下角的坐标。
Field | 含义 | 备注 |
---|---|---|
mTmpParentFrame | 父窗口区域 | 一般就是mTmpDisplayFrame |
mTmpDisplayFrame | Window完整展示区域 | StatusBar一般是全屏,因为StatusBar是下拉展示全屏 |
mTmpOverscanFrame | 左上角(overscanLeft,overscanTop) 右下角(displayWidth-overscanRight,displayHeight-overscanBottom) |
去掉overscan 区域以后的区域 |
mTmpContentFrame | Window内容区域 | -- |
mTmpVisibleFrame | Window可见区域 | -- |
mTmpDecorFrame | ||
mTmpStableFrame | 左上角(0,statusBarHeight) 右下角(displayWidth,displayHeight-navigationBarHeight) |
没有计算overscan |
mTmpNavigationFrame | NavigationBar对应的坐标 | |
mTmpOutsetFrame | Surface区域外 |
WindowManagerService计算窗口位置,大小流程
WMS测量的流程图。
源码分析
WMS.relayoutWindow
//WindowManagerService.java
1907 public int relayoutWindow(Session session, IWindow client, int seq,
1908 WindowManager.LayoutParams attrs, int requestedWidth,
1909 int requestedHeight, int viewVisibility, int flags,
1910 Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
1911 Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
1912 MergedConfiguration mergedConfiguration, Surface outSurface) {
1913 int result = 0;
1914 boolean configChanged; // 配置是否发生了变化
1915 boolean hasStatusBarPermission =
1916 mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
1917 == PackageManager.PERMISSION_GRANTED;
1918
1919 long origId = Binder.clearCallingIdentity();
1920 final int displayId;
1921 synchronized(mWindowMap) { //访问到共享资源,注意同步
1922 //TODO: step1, 根据IWindow找到对应的WindowState,
1923 WindowState win = windowForClientLocked(session, client, false);
1924 if (win == null) {
1925 return 0;
1926 }
1927 displayId = win.getDisplayId();
1928
1929 WindowStateAnimator winAnimator = win.mWinAnimator;
1930 if (viewVisibility != View.GONE) {
1931 win.setRequestedSize(requestedWidth, requestedHeight);
1932 }
//..... 省略很多
2014 final int oldVisibility = win.mViewVisibility;
2015 win.mViewVisibility = viewVisibility;
2016 if (DEBUG_SCREEN_ON) {
2017 RuntimeException stack = new RuntimeException();
2018 stack.fillInStackTrace();
2019 Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
2020 + " newVis=" + viewVisibility, stack);
2021 }
2022 if (viewVisibility == View.VISIBLE &&
2023 (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
2024 || !win.mAppToken.isClientHidden())) {
2025
2026 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
2027
2028 // We are about to create a surface, but we didn't run a layout yet. So better run
2029 // a layout now that we already know the right size, as a resize call will make the
2030 // surface transaction blocking until next vsync and slow us down.
2031 // TODO: Ideally we'd create the surface after running layout a bit further down,
2032 // but moving this seems to be too risky at this point in the release.
2033 //
2034 // 我们即将创建一个Surface,但我们还没有运行布局,所以最好在我们已经知道正确大小
2035 // 的情况下运行布局,因为调整大小调用会使表面失误受阻,直到下一次vsync并减慢我们
2036 // 的速度。
2037 // 理想情况下,我们会在将布局进一步向下运行后创建Surface,但是在版本发布点上
2038 // 移动它似乎风险太大。
2039 if (win.mLayoutSeq == -1) {
2040 win.setDisplayLayoutNeeded();
2041 //TODO: step2: 销毁surface, 重新计算图层,计算触电区域
2042 mWindowPlacerLocked.performSurfacePlacement(true);
2043 }
2044 result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
2045 oldVisibility);
2046
2047 try {
2048 //TODO: step3: 这里创建Surface, 使用SurfaceControl和 SurfaceFlinger通信
2049 result = createSurfaceControl(outSurface, result, win, winAnimator);
2050 } catch (Exception e) {
2051 mInputMonitor.updateInputWindowsLw(true /*force*/);
2052
2053 Slog.w(TAG_WM, "Exception thrown when creating surface for client "
2054 + client + " (" + win.mAttrs.getTitle() + ")",
2055 e);
2056 Binder.restoreCallingIdentity(origId);
2057 return 0;
2058 }
2059 if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
2060 focusMayChange = isDefaultDisplay;
2061 }
2062 if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
2063 setInputMethodWindowLocked(win);
2064 imMayMove = true;
2065 }
2066 win.adjustStartingWindowFlags();
//.... 省略很多
2101 if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
2102 // We already told the client to go invisible, but the message may not be
2103 // handled yet, or it might want to draw a last frame. If we already have a
2104 // surface, let the client use that, but don't create new surface at this point.
2105 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
2106 winAnimator.mSurfaceController.getSurface(outSurface);
2107 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2108 } else {
2109 if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
2110
2111 try {
2112 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
2113 + win.mAttrs.getTitle());
2114 outSurface.release();
2115 } finally {
2116 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2117 }
2118 }
2119
2120 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2121 }
2122
2123 if (focusMayChange) {
2124 //System.out.println("Focus may change: " + win.mAttrs.getTitle());
2125 //TODO: step4: window焦点发生变化, 重新计算窗口大小
2126 if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
2127 false /*updateInputWindows*/)) {
2128 imMayMove = false;
2129 }
2130 //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
2131 }
2132
2133 // updateFocusedWindowLocked() already assigned layers so we only need to
2134 // reassign them at this point if the IM window state gets shuffled
2135 boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
2136 final DisplayContent dc = win.getDisplayContent();
2137 if (imMayMove) {
2138 dc.computeImeTarget(true /* updateImeTarget */);
2139 if (toBeDisplayed) {
2140 // Little hack here -- we -should- be able to rely on the function to return
2141 // true if the IME has moved and needs its layer recomputed. However, if the IME
2142 // was hidden and isn't actually moved in the list, its layer may be out of data
2143 // so we make sure to recompute it.
2144 // TODO: step5 计算z-order层级
2145 dc.assignWindowLayers(false /* setLayoutNeeded */);
2146 }
2147 }
//.... 省略很多代码
2165 // We may be deferring layout passes at the moment, but since the client is interested
2166 // in the new out values right now we need to force a layout.
2167 mWindowPlacerLocked.performSurfacePlacement(true /* force */);
2168 if (toBeDisplayed && win.mIsWallpaper) {
2169 DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
2170 dc.mWallpaperController.updateWallpaperOffset(
2171 win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
2172 }
2173 if (win.mAppToken != null) {
2174 win.mAppToken.updateReportedVisibilityLocked();
2175 }
2176 if (winAnimator.mReportSurfaceResized) {
2177 winAnimator.mReportSurfaceResized = false;
2178 result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
2179 }
2180 if (mPolicy.isNavBarForcedShownLw(win)) {
2181 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR;
2182 }
2183 if (!win.isGoneForLayoutLw()) {
2184 win.mResizedWhileGone = false;
2185 }
2186 //TODO: step6, 设置已经计算好的边界,并返回给view.
2187 outFrame.set(win.mCompatFrame);
2188 outOverscanInsets.set(win.mOverscanInsets);
2189 outContentInsets.set(win.mContentInsets);
2190 win.mLastRelayoutContentInsets.set(win.mContentInsets);
2191 outVisibleInsets.set(win.mVisibleInsets);
2192 outStableInsets.set(win.mStableInsets);
2193 outOutsets.set(win.mOutsets);
2194 outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
2195 if (localLOGV) Slog.v(
2196 TAG_WM, "Relayout given client " + client.asBinder()
2197 + ", requestedWidth=" + requestedWidth
2198 + ", requestedHeight=" + requestedHeight
2199 + ", viewVisibility=" + viewVisibility
2200 + "\nRelayout returning frame=" + outFrame
2201 + ", surface=" + outSurface);
2202
2203 if (localLOGV || DEBUG_FOCUS) Slog.v(
2204 TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
2205
2206 result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
2207
2208 mInputMonitor.updateInputWindowsLw(true /*force*/);
2209
2210 if (DEBUG_LAYOUT) {
2211 Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
2212 }
2213 win.mInRelayout = false;
2214 }
2215
2216 if (configChanged) {
2217 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: sendNewConfiguration");
2218 sendNewConfiguration(displayId);
2219 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2220 }
2221 Binder.restoreCallingIdentity(origId);
2222 return result;
2223 }
relayout大致上要做了以下的事情:
step1#1923行, 通过IWindow找到对应的WindowState,并且获取各种参数。
step2#2042行, 销毁surface, 重新计算图层,输入焦点区域
step3#2049行,创建Surface对象,通关SurfaceControl和SurfaceFlinger通信
step4#2126行,更新焦点窗口区域,重新计算所有窗口的位置,大小,焦点区域。
step5#2145行,计算z-order层级
step6#2186行, 保持计算好的window位置,大小,和创建好的surface返回给viewRootImpl。
relayout的方法有点长,本次我们将关注这一部分核心的逻辑。分别是两个方法:
- 1.mWindowPlacerLocked.performSurfacePlacement 当窗体出现了变更,需要重新设置DisplayContent的各种参数,销毁不用的Surface,重新计算层级,计算触点区域等等
- 2.updateFocusedWindowLocked 当发现Window可能发生变化,则重新计算窗口大小。
WMS.updateFocusedWindowLocked
//WindowManagerService.java
5886 // TODO: Move to DisplayContent
5887 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
5888 WindowState newFocus = mRoot.computeFocusedWindow();
5889 if (mCurrentFocus != newFocus) {
5890 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
5891 // This check makes sure that we don't already have the focus
5892 // change message pending.
5893 mH.removeMessages(H.REPORT_FOCUS_CHANGE);
5894 mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
5895 // TODO(multidisplay): Focused windows on default display only.
5896 final DisplayContent displayContent = getDefaultDisplayContentLocked();
5897 boolean imWindowChanged = false;
5898 //step1, 输入窗口不等于空,或者输入法窗口焦点发生变化,重新找的焦点窗口
5899 if (mInputMethodWindow != null) {
5900 final WindowState prevTarget = mInputMethodTarget;
5901 final WindowState newTarget =
5902 displayContent.computeImeTarget(true /* updateImeTarget*/);
5903
5904 imWindowChanged = prevTarget != newTarget;
5905
5906 if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
5907 && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
5908 final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;
5909 displayContent.assignWindowLayers(false /* setLayoutNeeded */);
5910 imWindowChanged |=
5911 prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;
5912 }
5913 }
5914
5915 if (imWindowChanged) {
5916 mWindowsChanged = true;
5917 displayContent.setLayoutNeeded();
5918 newFocus = mRoot.computeFocusedWindow();
5919 }
5920
5921 if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
5922 mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
5923 final WindowState oldFocus = mCurrentFocus;
5924 mCurrentFocus = newFocus;
5925 mLosingFocus.remove(newFocus);
5926
5927 if (mCurrentFocus != null) {
5928 mWinAddedSinceNullFocus.clear();
5929 mWinRemovedSinceNullFocus.clear();
5930 }
5931
5932 int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
5933
5934 //step2, 如果输入法焦点出现了变化,且当前模式是UPDATE_FOCUS_PLACING_SURFACES
5935 // (需要强制绘制),则重新计算窗体的大小,否则直接做一次层级变化
5936 if (imWindowChanged && oldFocus != mInputMethodWindow) {
5937 // Focus of the input method window changed. Perform layout if needed.
5938 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
5939 displayContent.performLayout(true /*initial*/, updateInputWindows);
5940 focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
5941 } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
5942 // Client will do the layout, but we need to assign layers
5943 // for handleNewWindowLocked() below.
5944 displayContent.assignWindowLayers(false /* setLayoutNeeded */);
5945 }
5946 }
5947
5948 //step3, 一般情况下,如果UPDATE_FOCUS_PLACING_SURFACES这个模式,则需要performLayout
5949 // 重新测量窗体的各个边距大小
5950 if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
5951 // The change in focus caused us to need to do a layout. Okay.
5952 displayContent.setLayoutNeeded();
5953 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
5954 displayContent.performLayout(true /*initial*/, updateInputWindows);
5955 }
5956 }
5957
5958 //step4, 如果UPDATE_FOCUS_WILL_ASSIGN_LAYERS模式,则需要处理输入焦点
5959 if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
5960 // If we defer assigning layers, then the caller is responsible for
5961 // doing this part.
5962 mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
5963 }
5964
5965 displayContent.adjustForImeIfNeeded();
5966
5967 // We may need to schedule some toast windows to be removed. The toasts for an app that
5968 // does not have input focus are removed within a timeout to prevent apps to redress
5969 // other apps' UI.
5970 displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
5971
5972 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
5973 return true;
5974 }
5975 return false;
5976 }
step1#5899,首先如果发现有输入法弹窗则重新计算层级,或者说如果输入法焦点窗口发生变化,也要从RootWindowContainer找到当前的焦点窗口。
step2#5936, 如果输入法焦点出现了变化,且当前的模式是UPDATE_FOCUS_PLACING_SURFACES(需要强制重绘)则要重新计算窗体大小,否则则直接做一次层级变化即可。
step3#5952, 一般的情况下,如果UPDATE_FOCUS_PLACING_SURFACES这个模式,则需要performLayout重新测量窗体各个边距大小
step4#5962, 不是UPDATE_FOCUS_WILL_ASSIGN_LAYERS模式,则则需要处理触点焦点边距。
DisplayContent.performLayout
//DisplayContent.java
2779 void performLayout(boolean initial, boolean updateInputWindows) {
2780 if (!isLayoutNeeded()) {
2781 return;
2782 }
2783 clearLayoutNeeded();
2784
2785 final int dw = mDisplayInfo.logicalWidth;
2786 final int dh = mDisplayInfo.logicalHeight;
2787
2788 if (DEBUG_LAYOUT) {
2789 Slog.v(TAG, "-------------------------------------");
2790 Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
2791 }
2792
2793 //step1, 开始布局,准备初始化变量
2794 mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation,
2795 getConfiguration().uiMode);
2796 if (isDefaultDisplay) {
2797 // Not needed on non-default displays.
2798 mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
2799 mService.mScreenRect.set(0, 0, dw, dh);
2800 }
2801
2802 mService.mPolicy.getContentRectLw(mContentRect);
2803
2804 int seq = mService.mLayoutSeq + 1;
2805 if (seq < 0) seq = 0;
2806 mService.mLayoutSeq = seq;
2807
2808 // Used to indicate that we have processed the dream window and all additional windows are
2809 // behind it.
2810 mTmpWindow = null;
2811 mTmpInitial = initial;
2812
2813 //step2, 首先测量那些没有绑定父窗口的窗口
2814 // First perform layout of any root windows (not attached to another window).
2815 forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
2816
2817 // Used to indicate that we have processed the dream window and all additional attached
2818 // windows are behind it.
2819 mTmpWindow2 = mTmpWindow;
2820 mTmpWindow = null;
2821
2822 //step3. 测量子窗口
2823 // Now perform layout of attached windows, which usually depend on the position of the
2824 // window they are attached to. XXX does not deal with windows that are attached to windows
2825 // that are themselves attached.
2826 forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
2827
2828 //step4, 更新输入法焦点窗口
2829 // Window frames may have changed. Tell the input dispatcher about it.
2830 mService.mInputMonitor.layoutInputConsumers(dw, dh);
2831 mService.mInputMonitor.setUpdateInputWindowsNeededLw();
2832 if (updateInputWindows) {
2833 mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
2834 }
2835
2836 //step5, 完成布局测量
2837 mService.mPolicy.finishLayoutLw();
2838 mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
2839 }
step1#2794, 开始layout,准备初始化变量
step2#2815, 首先测量那些没有绑定父窗口的窗口
step3#2826, 测量子窗口
step4#2830, 更新输入法焦点窗口
step5#2837,完成测量
PhoneWindowManager.beginLayoutLw开始测量大小与边距
//PhoneWindowManager.java
4485 public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
4486 int displayRotation, int uiMode) {
4487 mDisplayRotation = displayRotation;
4488 final int overscanLeft, overscanTop, overscanRight, overscanBottom;
4489
4490 if (isDefaultDisplay) {
4491 switch (displayRotation) {
4492 case Surface.ROTATION_90:
4493 overscanLeft = mOverscanTop;
4494 overscanTop = mOverscanRight;
4495 overscanRight = mOverscanBottom;
4496 overscanBottom = mOverscanLeft;
4497 break;
4498 case Surface.ROTATION_180:
4499 overscanLeft = mOverscanRight;
4500 overscanTop = mOverscanBottom;
4501 overscanRight = mOverscanLeft;
4502 overscanBottom = mOverscanTop;
4503 break;
// 省略一些代码
4523 mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
4524 mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
4525
4526 mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
4527 mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
4528 mSystemLeft = 0;
4529 mSystemTop = 0;
4530 mSystemRight = displayWidth;
4531 mSystemBottom = displayHeight;
4532 mUnrestrictedScreenLeft = overscanLeft;
4533 mUnrestrictedScreenTop = overscanTop;
4534 mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
4535 mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
4536 mRestrictedScreenLeft = mUnrestrictedScreenLeft;
4537 mRestrictedScreenTop = mUnrestrictedScreenTop;
4538 mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
4539 mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
4540 mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
4541 = mCurLeft = mUnrestrictedScreenLeft;
4542 mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
4543 = mCurTop = mUnrestrictedScreenTop;
4544 mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
4545 = mCurRight = displayWidth - overscanRight;
4546 mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
4547 = mCurBottom = displayHeight - overscanBottom;
4548 mDockLayer = 0x10000000;
4549 mStatusBarLayer = -1;
4550
4551 // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
4552 final Rect pf = mTmpParentFrame;
4553 final Rect df = mTmpDisplayFrame;
4554 final Rect of = mTmpOverscanFrame;
4555 final Rect vf = mTmpVisibleFrame;
4556 final Rect dcf = mTmpDecorFrame;
4557 //step1, 初始化pf,df,of, vf, dcf 窗口大小
4558 pf.left = df.left = of.left = vf.left = mDockLeft;
4559 pf.top = df.top = of.top = vf.top = mDockTop;
4560 pf.right = df.right = of.right = vf.right = mDockRight;
4561 pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
4562 dcf.setEmpty(); // Decor frame N/A for system bars.
//省略一些代码。。。
4583 // When the navigation bar isn't visible, we put up a fake
4584 // input window to catch all touch events. This way we can
4585 // detect when the user presses anywhere to bring back the nav
4586 // bar and ensure the application doesn't see the event.
4587 if (navVisible || navAllowedHidden) {
4588 if (mInputConsumer != null) {
4589 mHandler.sendMessage(
4590 mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
4591 mInputConsumer = null;
4592 }
4593 } else if (mInputConsumer == null) {
4594 mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
4595 INPUT_CONSUMER_NAVIGATION,
4596 (channel, looper) -> new HideNavInputEventReceiver(channel, looper));
4597 // As long as mInputConsumer is active, hover events are not dispatched to the app
4598 // and the pointer icon is likely to become stale. Hide it to avoid confusion.
4599 InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
4600 }
4601
4602 // For purposes of positioning and showing the nav bar, if we have
4603 // decided that it can't be hidden (because of the screen aspect ratio),
4604 // then take that into account.
4605 navVisible |= !canHideNavigationBar();
4606
4607 //step2, 计算导航栏layout
4608 boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
4609 displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
4610 navAllowedHidden, statusBarExpandedNotKeyguard);
4611 if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
4612 mDockLeft, mDockTop, mDockRight, mDockBottom));
4613 //step3, 计算状态栏layout
4614 updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
4615 if (updateSysUiVisibility) {
4616 updateSystemUiVisibilityLw();
4617 }
4618 }
4619 }
PhoneWindowManager.layoutWindowLw计算pf,df,of,cf,vf,
//PhoneWindowManager.java
4917 public void layoutWindowLw(WindowState win, WindowState attached) {
4918 // We've already done the navigation bar and status bar. If the status bar can receive
4919 // input, we need to layout it again to accomodate for the IME window.
4920 if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
4921 return;
4922 }
4923 final WindowManager.LayoutParams attrs = win.getAttrs();
4924 final boolean isDefaultDisplay = win.isDefaultDisplay();
4925 final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
4926 (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
4927 if (needsToOffsetInputMethodTarget) {
4928 if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
4929 offsetInputMethodWindowLw(mLastInputMethodWindow);
4930 }
4931
// 省略代码...
5408 Slog.v(TAG, "Compute frame " + attrs.getTitle()
5409 + ": sim=#" + Integer.toHexString(sim)
5410 + " attach=" + attached + " type=" + attrs.type
5411 + String.format(" flags=0x%08x", fl)
5412 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
5413 + " of=" + of.toShortString()
5414 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
5415 + " dcf=" + dcf.toShortString()
5416 + " sf=" + sf.toShortString()
5417 + " osf=" + (osf == null ? "null" : osf.toShortString()));
5418
5419 win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf);
5420
5421 // Dock windows carve out the bottom of the screen, so normal windows
5422 // can't appear underneath them.
5423 if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw()
5424 && !win.getGivenInsetsPendingLw()) {
5425 setLastInputMethodWindowLw(null, null);
5426 offsetInputMethodWindowLw(win);
5427 }
5428 if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
5429 && !win.getGivenInsetsPendingLw()) {
5430 offsetVoiceInputWindowLw(win);
5431 }
5432 }
各种计算出... 临时变量pf,df,of,cf,vf后调用computeFrameLw方案
WindowState.computeFrameLw
//WindowState.java
735 public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
736 Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
737 Rect outsetFrame) {
738 if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
739 // This window is being replaced and either already got information that it's being
740 // removed or we are still waiting for some information. Because of this we don't
741 // want to apply any more changes to it, so it remains in this state until new window
742 // appears.
743 return;
744 }
745 mHaveFrame = true;
//...
800 // Denotes the actual frame used to calculate the insets and to perform the layout. When
801 // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
802 // insets temporarily. By the notion of a task having a different layout frame, we can
803 // achieve that while still moving the task around.
804 // 表示用于计算插图和执行布局的实际窗口大小,在可调整大小的dock模式下,我们希望冻结布局,
805 // 所以我们希望冻结插图,通过具有不同布局框架的任务概率,我们可以在移动任务的同时实现这一点。
806 // 我理解这两个变量是暂时把父窗口和显示窗口大小固定下来, 看后面怎么用的?
807 final Rect layoutContainingFrame;
808 final Rect layoutDisplayFrame;
809
810 // The offset from the layout containing frame to the actual containing frame.
811 final int layoutXDiff;
812 final int layoutYDiff;
813 //step1, 当全屏的时候,设置内容区域据说父亲区域,显示屏大小就是换地尽量的显示屏区域,
814 // 并且窗体没有位移
815 if (inFullscreenContainer || layoutInParentFrame()) {
816 // We use the parent frame as the containing frame for fullscreen and child windows
817 mContainingFrame.set(parentFrame);
818 mDisplayFrame.set(displayFrame);
819 layoutDisplayFrame = displayFrame;
820 layoutContainingFrame = parentFrame;
821 layoutXDiff = 0;
822 layoutYDiff = 0;
823 } else {
824 // 当不上全屏,或在父窗口内部的模式
825 getContainerBounds(mContainingFrame);
826 if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
827
828 // If the bounds are frozen, we still want to translate the window freely and only
829 // freeze the size.
830 Rect frozen = mAppToken.mFrozenBounds.peek();
831 mContainingFrame.right = mContainingFrame.left + frozen.width();
832 mContainingFrame.bottom = mContainingFrame.top + frozen.height();
833 }
//...
860 // 显示屏区域被设置成内容区域
861 mDisplayFrame.set(mContainingFrame);
862 // 计算偏移量,这个偏移量是根据临时内容区域变化来
863 layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
864 layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
865 layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
866 mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
867 // 合并所有的区域到显示屏区域中
868 subtractInsets(mDisplayFrame, layoutContainingFrame, displayFrame, mTmpRect);
869 if (!layoutInParentFrame()) {
870 subtractInsets(mContainingFrame, layoutContainingFrame, parentFrame, mTmpRect);
871 subtractInsets(mInsetFrame, layoutContainingFrame, parentFrame, mTmpRect);
872 }
873 layoutDisplayFrame = displayFrame;
874 layoutDisplayFrame.intersect(layoutContainingFrame);
875 }
876
877 final int pw = mContainingFrame.width();
878 final int ph = mContainingFrame.height();
879
880 if (!mParentFrame.equals(parentFrame)) {
881 //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
882 // + " to " + parentFrame);
883 mParentFrame.set(parentFrame);
884 mContentChanged = true;
885 }
886 if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
887 mLastRequestedWidth = mRequestedWidth;
888 mLastRequestedHeight = mRequestedHeight;
889 mContentChanged = true;
890 }
891
892 //step2, 保存ov, cf, vf, df, sf到winstate变量里面
893 mOverscanFrame.set(overscanFrame);
894 mContentFrame.set(contentFrame);
895 mVisibleFrame.set(visibleFrame);
896 mDecorFrame.set(decorFrame);
897 mStableFrame.set(stableFrame);
898 final boolean hasOutsets = outsetFrame != null;
899 if (hasOutsets) {
900 mOutsetFrame.set(outsetFrame);
901 }
//....
903 final int fw = mFrame.width();
904 final int fh = mFrame.height();
905
906 // 根据window Gravity计算边距
907 applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
908
909 // Calculate the outsets before the content frame gets shrinked to the window frame.
910 // 计算window外部添加区域,
911 if (hasOutsets) {
912 mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
913 Math.max(mContentFrame.top - mOutsetFrame.top, 0),
914 Math.max(mOutsetFrame.right - mContentFrame.right, 0),
915 Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
916 } else {
917 mOutsets.set(0, 0, 0, 0);
918 }
919
920 // Make sure the content and visible frames are inside of the
921 // final window frame.
922 // step3, 确保内容和可见区域,在最终的窗口内部
923 if (windowsAreFloating && !mFrame.isEmpty()) {
924 // For pinned workspace the frame isn't limited in any particular
925 // way since SystemUI controls the bounds. For freeform however
926 // we want to keep things inside the content frame.
927 final Rect limitFrame = task.inPinnedWorkspace() ? mFrame : mContentFrame;
928 // Keep the frame out of the blocked system area, limit it in size to the content area
929 // and make sure that there is always a minimum visible so that the user can drag it
930 // into a usable area..
931 final int height = Math.min(mFrame.height(), limitFrame.height());
932 final int width = Math.min(limitFrame.width(), mFrame.width());
933 final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
934 final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
935 MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
//...
970 //step4, 设置过扫苗区域(边距)
971 if (inFullscreenContainer && !windowsAreFloating) {
972 // Windows that are not fullscreen can be positioned outside of the display frame,
973 // but that is not a reason to provide them with overscan insets.
974 mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
975 Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
976 Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
977 Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
978 }
//...
1023 // Offset the actual frame by the amount layout frame is off.
1024 //step5, 设置位移区域
1025 mFrame.offset(-layoutXDiff, -layoutYDiff);
1026 mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
1027 mContentFrame.offset(-layoutXDiff, -layoutYDiff);
1028 mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
1029 mStableFrame.offset(-layoutXDiff, -layoutYDiff);
1030
1031 mCompatFrame.set(mFrame);
1032 //step5, 设置屏幕内容的缩放
1033 if (mEnforceSizeCompat) {
1034 // If there is a size compatibility scale being applied to the
1035 // window, we need to apply this to its insets so that they are
1036 // reported to the app in its coordinate space.
1037 mOverscanInsets.scale(mInvGlobalScale);
1038 mContentInsets.scale(mInvGlobalScale);
1039 mVisibleInsets.scale(mInvGlobalScale);
1040 mStableInsets.scale(mInvGlobalScale);
1041 mOutsets.scale(mInvGlobalScale);
1042
1043 // Also the scaled frame that we report to the app needs to be
1044 // adjusted to be in its coordinate space.
1045 mCompatFrame.scale(mInvGlobalScale);
1046 }
1047
1048 //step7, 更新壁纸的位移
1049 if (mIsWallpaper && (fw != mFrame.width() || fh != mFrame.height())) {
1050 final DisplayContent displayContent = getDisplayContent();
1051 if (displayContent != null) {
1052 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
1053 getDisplayContent().mWallpaperController.updateWallpaperOffset(
1054 this, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
1055 }
1056 }
1057
1058 if (DEBUG_LAYOUT || localLOGV) Slog.v(TAG,
1059 "Resolving (mRequestedWidth="
1060 + mRequestedWidth + ", mRequestedheight="
1061 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
1062 + "): frame=" + mFrame.toShortString()
1063 + " ci=" + mContentInsets.toShortString()
1064 + " vi=" + mVisibleInsets.toShortString()
1065 + " si=" + mStableInsets.toShortString()
1066 + " of=" + mOutsets.toShortString());
1067 }
computeFrameLw方法是主要Frame,这个是应用真实显示区域,确保在正确边界之内,同时计算Inset(边衬)区域。
WMS 到 SurfaceFlinger设置窗口位置
WindowSurfaceController.setPositionInTransaction --> SurfaceControl.setPosition() --> android_view_SurfaceControl.nativeSetPosition --> SurfaceComposerClient.setPosition()
Composer::closeGlobalTransactionImpl() --> SurfaceFlinger->setTransactionState() --> layer->setPosition() 设置到最终的layer上面。 layer是SurfaceFlinger最终合成图层使用。
总结:
也花了几天时间,把这个完成流程梳理了下,中间也忽略了很多细节,也参考了下写的很不错的博客,计算窗口的原理不是很复杂,就是细节特别多。后面根据自己的需求,比如我想把虚拟分辨率设置为3840x1080或3840x2160,然后随意调整activity(window)显示位置,让系统可以显示多个activity, 虽然当前分屏和多窗口可以做到,但是很容易导致UI适配不兼容问题。
参考资料
深入理解Android内核设计思想
Android 重学系列 WMS在Activity启动中的职责 计算窗体的大小(四)
Window大小位置测量以及View绘制流程
Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析