performTraversals()

  /**
     * performTraversals()函数正是系统内进行View树遍历工作的核心函数,该函数内部逻辑稍有复杂,
     * 一个函数代码长度约600行。虽然该函数的代码很长,但其主体逻辑却是很清晰的,其执行过程可简单
     * 概括为根据之前所有设置好的状态,判断是否需要重新计算视图大小 measure)、是否需要重新安置视
     * 图 的 位 置 layout),以及是否需要重绘 draw)视图,
     */
    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }
        /**一、处理mAttachlnfo的初始化,并根据resize和visibility改变的情况,给相应的变量赋值。
         *
         */

        if (host == null || !mAdded)
            return;

        mTraversalScheduled = false;
        mWillDrawSoon = true;
        boolean windowResizesToFitContent = false;
        boolean fullRedrawNeeded = mFullRedrawNeeded;
        boolean newSurface = false;
        boolean surfaceChanged = false;
        WindowManager.LayoutParams lp = mWindowAttributes;

        int desiredWindowWidth;
        int desiredWindowHeight;
        int childWidthMeasureSpec;
        int childHeightMeasureSpec;

        final View.AttachInfo attachInfo = mAttachInfo;

        final int viewVisibility = getHostVisibility();
        boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                || mNewSurfaceNeeded;

        float appScale = mAttachInfo.mApplicationScale;

        WindowManager.LayoutParams params = null;
        if (mWindowAttributesChanged) {
            mWindowAttributesChanged = false;
            surfaceChanged = true;
            params = lp;
        }
        Rect frame = mWinFrame;
        /**
         * ) 如果是第一次进行View树遍历,则对mAttachlnfo中的变量进行初始化。所谓的 “第一次”
         * mFirst变量是指,该窗口创建好以后的第一次显示,在后面整个运行过程中,都不是第一次,直到应用
         * 程序退出,该窗口被销毁为止。初始化后,调用host.dispatchAttachedToWindow(),参数包含mAttachlnfo,
         * 所有的子视图都将把mAttachlnfo的值复制到自己的mAttachlnfo变量中。
         */
        if (mFirst) {
            fullRedrawNeeded = true;
            mLayoutRequested = true;

            DisplayMetrics packageMetrics =
                mView.getContext().getResources().getDisplayMetrics();
            desiredWindowWidth = packageMetrics.widthPixels;
            desiredWindowHeight = packageMetrics.heightPixels;

            // For the very first time, tell the view hierarchy that it
            // is attached to the window.  Note that at this point the surface
            // object is not initialized to its backing store, but soon it
            // will be (assuming the window is visible).
            attachInfo.mSurface = mSurface;
            attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
                    lp.format == PixelFormat.RGBX_8888;
            attachInfo.mHasWindowFocus = false;
            attachInfo.mWindowVisibility = viewVisibility;
            attachInfo.mRecomputeGlobalAttributes = false;
            attachInfo.mKeepScreenOn = false;
            viewVisibilityChanged = false;
            mLastConfiguration.setTo(host.getResources().getConfiguration());
            host.dispatchAttachedToWindow(attachInfo, 0);
            //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);

        } else {
            /**
             * ) 如果不是第一次,则查看是否resize过,resize—般是用于输入窗口显示后,WmS通知客户
             * 端窗口调整窗口大小,resize发生后,客户窗口需要做以下工作。
             * • 将 fUllRedrawNeeded置为true,即需要全部重绘。
             * • 将 mLayoutRequested置为true,即需要重新为视图指定位置
             * • 将 windowResizesToFitContent置为true,该变量将在下面被使用
             */

            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
            //判断resize
            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                if (DEBUG_ORIENTATION) Log.v(TAG,
                        "View " + host + " resized to: " + frame);
                fullRedrawNeeded = true;
                mLayoutRequested = true;
                windowResizesToFitContent = true;
            }
        }
        /**
         * 如果窗口的visibility状态发生改变,比如窗口从后台切换到前台或者相反,则给mAttachlnfo
         * 中的变量 mWindowVisibility 赋新值,并调用 host.dispatchWindowVisibilityChanged()
         * 函数将这个变化信息传递给所有的子视图。
         */
        if (viewVisibilityChanged) {
            attachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                if (mUseGL) {
                    destroyGL();
                }
            }
            if (viewVisibility == View.GONE) {
                // After making a window gone, we will count it as being
                // shown for the first time the next time it gets focus.
                mHasHadWindowFocus = false;
            }
        }

        boolean insetsChanged = false;
        /**
         * 如果需要重新布局,即 mRequestedLayout变量为true,则首先需要重新计算所有视图的大小。
         * 换句话说,要重新layout,就必须重新measure。
         */
        if (mLayoutRequested) {
            // Execute enqueued actions on every layout in case a view that was detached
            // enqueued an action after being detached
            getRunQueue().executeActions(attachInfo.mHandler);
            /**
             * )如果 mFirst为true,则调用 host.fitSystemWindow(attachlnfo.mContentlnsets)。
             * 参数 mContentlnsets
             * 是 WmS为应用程序设置的“嵌入区”,inset本 意 是 “嵌入”、“插图”,Content是指视图区用于真正显
             * 示内容的区域,所以,mContentlnsets的大小一般是指状态栏窗口的大小,当应用程序全屏运行时,
             * mContentlnsets的大小为0。这里有意思的是该变量的类型是一个Rect,而这个insets的大小是由WmS
             * 指定的,问题是WmS如何把这个矩形信息传递给客户窗口呢?答案就是Rect实现了 Pacelable接口,
             * 客户端可以创建一个 Rect变量,并把这个变量传递给WmS,然后由WmS修改后再返回。
             * fitSystemWindow()函数的作用是告沂窗口中所有子视图根据该Inset调整自己的布局,实际上就是用
             * inset大小改变视图的mPaddingXXX的值
             */
            if (mFirst) {
                host.fitSystemWindows(mAttachInfo.mContentInsets);
                // make sure touch mode code executes by setting cached value
                // to opposite of the added touch mode.
                mAttachInfo.mInTouchMode = !mAddedTouchMode;
                ensureTouchModeLocally(mAddedTouchMode);
            } else {
                /**
                 * 如果不是第一次,则继续执行以下流程。
                 * (2)判 断 insets是否有变化,如果变化了,也需要调用host.fitSystemWindow()通知窗口中的视图
                 * 修改mPaddingXXX参数值。
                 */

                if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
                    host.fitSystemWindows(mAttachInfo.mContentInsets);
                    insetsChanged = true;
                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
                            + mAttachInfo.mContentInsets);
                }
                /**
                 * 如果mAttachInfo的mVisibleInsets有改变,则将mPendingVisibleInsets
                 * 赋值给mVisibleInsets,该矩形变量一般为空。
                 */
                if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
                            + mAttachInfo.mVisibleInsets);
                }
                if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                        || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    windowResizesToFitContent = true;

                    DisplayMetrics packageMetrics =
                        mView.getContext().getResources().getDisplayMetrics();
                    desiredWindowWidth = packageMetrics.widthPixels;
                    desiredWindowHeight = packageMetrics.heightPixels;
                }
            }
            /**
             * 根据窗口宽度获得子视图大小的“测量标准”,其中宽度标准和高度标准分别保存到变量
             * childWidthMeasureSpec 和 childHeightMeasureSpec,以下简称 widthSpec 和 heightSpec。而这两个变量的
             * 本步操作中,有三个相关的变量容易混淆,一 个 是 lp,一 个 是 desiredWindowWidth,另一个是
             * measureSpec。
             *lp 变量代表的是根视图的LayoutParams、lp.width或 者 lp.height直接来源于用户的定义,比如
             * WRAP—CONTENT、MATCH_PARENT 等。
             * desiredWindowWidth是 指 实 际 窗 口 在 屏 幕 上 的 大 小 , 默 认 情 况 下 就 是 屏 幕 大 小 , 即
             * displayMetrics.widthPixels, 当窗口大小改变后,其值将变为mWinFrame的大小。比如当有输入法窗口
             * 时,mWinFrame的大小是屏幕大小减去输入法窗口的大小。
             * measureSpec 是调用 getRootMeasureSpec()函数的返回值,分两种,分别是 MeasureSpec.EXACTLY
             * 和 MeasureSpec.AT_MOST, 这两个常量分别是 0x4000 0000 和 0x8000 0000。measureSpec 是调用
             * MeasureSpec.makeMeasureSpec (size,类型 产生真正的 measureSpec 值,size 代表了真正的大小,换
             * 句话说,该变量的高16位代表类型,低 16位代表实际大小。measureSpec将作为测量子视图大小的标
             * 准,
             */

            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);

            // Ask host how big it wants to be
            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
                    "Measuring " + host + " in display " + desiredWindowWidth
                    + "x" + desiredWindowHeight + "...");
            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            if (DBG) {
                System.out.println("======================================");
                System.out.println("performTraversals -- after measure");
                host.debug();
            }
        }
        /**
         * 查 看 mAttachlnfo中 的 mRecomputeGlobalAttributes是 否 为 true。该变
         * 量的含义是有子视图的
         * visibility等属性发生过了改变, ViewRoot需要重新获取这些属性,于是调用
         * host.dispatchCollectViewAttributes(O) , 参数 0 代表 View.VISIBILITY
         */
        if (attachInfo.mRecomputeGlobalAttributes) {
            //Log.i(TAG, "Computing screen on!");
            attachInfo.mRecomputeGlobalAttributes = false;
            boolean oldVal = attachInfo.mKeepScreenOn;
            attachInfo.mKeepScreenOn = false;
            host.dispatchCollectViewAttributes(0);
            if (attachInfo.mKeepScreenOn != oldVal) {
                params = lp;
                //Log.i(TAG, "Keep screen on changed: " + attachInfo.mKeepScreenOn);
            }
        }
        /**
         * 如果是第一次,或者发生了 visibility变化,则需要修正lp中的 softlnputMode。当然这只是在
         * softlnputMode没有指定的情况下, 如果指定了就不用修改。
         */
        if (mFirst || attachInfo.mViewVisibilityChanged) {
            attachInfo.mViewVisibilityChanged = false;
            int resizeMode = mSoftInputMode &
                    WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
            // If we are in auto resize mode, then we need to determine
            // what mode to use now.
            if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
                final int N = attachInfo.mScrollContainers.size();
                for (int i=0; i<N; i++) {
                    if (attachInfo.mScrollContainers.get(i).isShown()) {
                        resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
                    }
                }
                if (resizeMode == 0) {
                    resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
                }
                if ((lp.softInputMode &
                        WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
                    lp.softInputMode = (lp.softInputMode &
                            ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
                            resizeMode;
                    params = lp;
                }
            }
        }

        if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
            if (!PixelFormat.formatHasAlpha(params.format)) {
                params.format = PixelFormat.TRANSLUCENT;
            }
        }

        boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
            && ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
                || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.width() < desiredWindowWidth && frame.width() != mWidth)
                || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                        frame.height() < desiredWindowHeight && frame.height() != mHeight));

        final boolean computesInternalInsets =
                attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
        boolean insetsPending = false;
        int relayoutResult = 0;
        /**
         * 无论是resize还 是 inset变化了,或者是窗口的visibility发生变化了,都意味着需要重新设置
         * 客户窗口的大小,并重新计算窗口中视图的大小。请注意区分本步操作和第2 步操作的区别,第 1 步
         * 是指窗口内部视图变化引起的重新计算大小,而本步是指窗口本身大小发生了变化。
         */
        if (mFirst || windowShouldResize || insetsChanged
                || viewVisibilityChanged || params != null) {

            if (viewVisibility == View.VISIBLE) {
                // If this window is giving internal insets to the window
                // manager, and it is being added or changing its visibility,
                // then we want to first give the window manager "fake"
                // insets to cause it to effectively ignore the content of
                // the window during layout.  This avoids it briefly causing
                // other windows to resize/move based on the raw frame of the
                // window, waiting until we can finish laying out this window
                // and get back to the window manager with the ultimately
                // computed insets.
                insetsPending = computesInternalInsets
                        && (mFirst || viewVisibilityChanged);

                if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
                    if (params == null) {
                        params = mWindowAttributes;
                    }
                    mGlWanted = true;
                }
            }

            if (mSurfaceHolder != null) {
                mSurfaceHolder.mSurfaceLock.lock();
                mDrawingAllowed = true;
            }
            
            boolean initialized = false;
            boolean contentInsetsChanged = false;
            boolean visibleInsetsChanged;
            boolean hadSurface = mSurface.isValid();
            try {
                int fl = 0;
                if (params != null) {
                    fl = params.flags;
                    if (attachInfo.mKeepScreenOn) {
                        params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                    }
                }
                if (DEBUG_LAYOUT) {
                    Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
                            host.mMeasuredHeight + ", params=" + params);
                }
                /**
                 * (1 )调用relayoutWindow(),该函数内部则会调用sWindowSession.relayout()请 求 WmS按照指定的
                 * 大小重新分配窗口大小,
                 * 最后一个参数mSurfkce是客户窗口创建的,WmS内部将为该Surface对象分配真正的显存, 等该
                 * 函数返回后,应用程序就可以在该Surface中绘制了
                 *
                 * 另外,参 数 mPendingContentlnsets、mPendingVisblelnsets都是输出参数,WmS会给这些变量中填
                 * 入新值。
                 */
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

                if (params != null) {
                    params.flags = fl;
                }

                if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
                        + " content=" + mPendingContentInsets.toShortString()
                        + " visible=" + mPendingVisibleInsets.toShortString()
                        + " surface=" + mSurface);

                if (mPendingConfiguration.seq != 0) {
                    if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
                            + mPendingConfiguration);
                    updateConfiguration(mPendingConfiguration, !mFirst);
                    mPendingConfiguration.seq = 0;
                }
                
                contentInsetsChanged = !mPendingContentInsets.equals(
                        mAttachInfo.mContentInsets);
                visibleInsetsChanged = !mPendingVisibleInsets.equals(
                        mAttachInfo.mVisibleInsets);
                /**
                 * (2)如 果 mPendingContentlnsets发生了改变,则需
                 * 要调用fitSystemWindow()通知所有子窗口根据
                 * 这种改变调整自身的大小
                 */
                if (contentInsetsChanged) {
                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
                    host.fitSystemWindows(mAttachInfo.mContentInsets);
                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
                            + mAttachInfo.mContentInsets);
                }
                /**
                 * 如果mPendingVisibleInsets发生了改变,则需要将改变的值赋给mAttachInfo的
                 * mVisibleInsets
                 */
                if (visibleInsetsChanged) {
                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
                            + mAttachInfo.mVisibleInsets);
                }
                /**
                 * 如果之前Surface无效,而此时有效了,则需要设置两个变量的值,一个是设置newSurface
                 * 为 true,另一个是设置fullRedrawNeeded为 true。本步中的条件的判断有点意思,
                 *
                 * 其中变量hadSurface是在调用relayoutWindow()之前赋值的,其值为mSurface.isValide()的返回值。
                 * 这也就是为什么这个变量叫做hadSurface而不是hasSurface的原因,因为它保存的是“之前” 是否有。
                 * 而当调用relayoutWindow()后,再次调用mSurface.isValid()肯定会返回true。所以,本步条件成立的情
                 * 况实际上就是当mFirst为 true时发生。
                 */
                if (!hadSurface) {
                    if (mSurface.isValid()) {
                        // If we are creating a new surface, then we need to
                        // completely redraw it.  Also, when we get to the
                        // point of drawing it we will hold off and schedule
                        // a new traversal instead.  This is so we can tell the
                        // window manager about all of the windows being displayed
                        // before actually drawing them, so it can display then
                        // all at once.
                        newSurface = true;
                        fullRedrawNeeded = true;
                        mPreviousTransparentRegion.setEmpty();

                        if (mGlWanted && !mUseGL) {
                            initializeGL();
                            initialized = mGlCanvas != null;
                        }
                    }
                } else if (!mSurface.isValid()) {
                    // If the surface has been removed, then reset the scroll
                    // positions.
                    mLastScrolledFocus = null;
                    mScrollY = mCurScrollY = 0;
                    if (mScroller != null) {
                        mScroller.abortAnimation();
                    }
                }
            } catch (RemoteException e) {
            }
            
            if (DEBUG_ORIENTATION) Log.v(
                    TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
            /**
             * 给 mAttachlnfo 中变量 mWindowLeft 和 mWindowTop 赋新值。该新值保存在 mWinFrame 中
             * 而该变量是在调用sWindowSession.relayout()时作为参数传递给WmS的,WmS会对该值进行修改。
             */
            attachInfo.mWindowLeft = frame.left;
            attachInfo.mWindowTop = frame.top;

            // !!FIXME!! This next section handles the case where we did not get the
            // window size we asked for. We should avoid this by getting a maximum size from
            // the window session beforehand.
            mWidth = frame.width();
            mHeight = frame.height();

            /**
             * 处理 SurfaceHolder.Callback 逻辑
             */
            if (mSurfaceHolder != null) {
                // The app owns the surface; tell it about what is going on.
                if (mSurface.isValid()) {
                    // XXX .copyFrom() doesn't work!
                    //mSurfaceHolder.mSurface.copyFrom(mSurface);
                    mSurfaceHolder.mSurface = mSurface;
                }
                mSurfaceHolder.mSurfaceLock.unlock();
                if (mSurface.isValid()) {
                    if (!hadSurface) {
                        mSurfaceHolder.ungetCallbacks();

                        mIsCreating = true;
                        mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                        if (callbacks != null) {
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        surfaceChanged = true;
                    }
                    if (surfaceChanged) {
                        mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
                                lp.format, mWidth, mHeight);
                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                        if (callbacks != null) {
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, lp.format,
                                        mWidth, mHeight);
                            }
                        }
                    }
                    mIsCreating = false;
                } else if (hadSurface) {
                    mSurfaceHolder.ungetCallbacks();
                    SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                    mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
                    if (callbacks != null) {
                        for (SurfaceHolder.Callback c : callbacks) {
                            c.surfaceDestroyed(mSurfaceHolder);
                        }
                    }
                    mSurfaceHolder.mSurfaceLock.lock();
                    // Make surface invalid.
                    //mSurfaceHolder.mSurface.copyFrom(mSurface);
                    mSurfaceHolder.mSurface = new Surface();
                    mSurfaceHolder.mSurfaceLock.unlock();
                }
            }
            
            if (initialized) {
                mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
                        (int) (mHeight * appScale + 0.5f));
            }

            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                    (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);

            /**
             * 最终,如果窗口尺寸发生了改变,贝U调用hostmeasureO重新计算窗口中视图的大小
             *
             */
            if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
                    || mHeight != host.mMeasuredHeight || contentInsetsChanged) {
                childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
                        + mWidth + " measuredWidth=" + host.mMeasuredWidth
                        + " mHeight=" + mHeight
                        + " measuredHeight" + host.mMeasuredHeight
                        + " coveredInsetsChanged=" + contentInsetsChanged);

                 // Ask host how big it wants to be
                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);

                // Implementation of weights from WindowManager.LayoutParams
                // We just grow the dimensions as needed and re-measure if
                // needs be
                int width = host.mMeasuredWidth;
                int height = host.mMeasuredHeight;
                boolean measureAgain = false;

                if (lp.horizontalWeight > 0.0f) {
                    width += (int) ((mWidth - width) * lp.horizontalWeight);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                            MeasureSpec.EXACTLY);
                    measureAgain = true;
                }
                if (lp.verticalWeight > 0.0f) {
                    height += (int) ((mHeight - height) * lp.verticalWeight);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                            MeasureSpec.EXACTLY);
                    measureAgain = true;
                }

                if (measureAgain) {
                    if (DEBUG_LAYOUT) Log.v(TAG,
                            "And hey let's measure once more: width=" + width
                            + " height=" + height);
                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                }

                mLayoutRequested = true;
            }
        }

        final boolean didLayout = mLayoutRequested;
        boolean triggerGlobalLayoutListener = didLayout
                || attachInfo.mRecomputeGlobalAttributes;
        /**
         * 接下来,根据以上步骤的执行结果,判断是否需要进行重新布局。比如当任意视图的大小发生
         * 变化时,它会影响其他视图的布局,如果是,则调用host.layout()进行真正的布局操作
         * 。该函数的内部
         * 详细流程参见后面小节
         */

        if (didLayout) {
            mLayoutRequested = false;
            mScrollMayChange = true;
            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
                TAG, "Laying out " + host + " to (" +
                host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
            long startTime = 0L;
            if (Config.DEBUG && ViewDebug.profileLayout) {
                startTime = SystemClock.elapsedRealtime();
            }
            host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);

            if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
                if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
                    throw new IllegalStateException("The view hierarchy is an inconsistent state,"
                            + "please refer to the logs with the tag "
                            + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
                }
            }

            if (Config.DEBUG && ViewDebug.profileLayout) {
                EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
            }

            // By this point all views have been sized and positionned
            // We can compute the transparent area

            if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
                // start out transparent
                // TODO: AVOID THAT CALL BY CACHING THE RESULT?
                host.getLocationInWindow(mTmpLocation);
                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
                        mTmpLocation[0] + host.mRight - host.mLeft,
                        mTmpLocation[1] + host.mBottom - host.mTop);

                host.gatherTransparentRegion(mTransparentRegion);
                if (mTranslator != null) {
                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
                }

                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                    mPreviousTransparentRegion.set(mTransparentRegion);
                    // reconfigure window manager
                    try {
                        sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
                    } catch (RemoteException e) {
                    }
                }
            }

            if (DBG) {
                System.out.println("======================================");
                System.out.println("performTraversals -- after setFrame");
                host.debug();
            }
        }

        if (triggerGlobalLayoutListener) {
            attachInfo.mRecomputeGlobalAttributes = false;
            attachInfo.mTreeObserver.dispatchOnGlobalLayout();
        }

        /**
         * 如果根视图包含一个 insets变化的监听者,
         * 那么,需要执行监听者指定的动作,即调用
         * attachInfo.mTreeObserver.dispatchOnComputeInternalInsets( )
         * 。 执行完毕后,监听者将设置新的 insets 值,
         * 如 果 insets发生了变化,则需要把这个变化报告给WmS,
         * 即调用sWindowSession.setInsets()函数。
         */

        if (computesInternalInsets) {
            ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
            final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
            final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
            givenContent.left = givenContent.top = givenContent.right
                    = givenContent.bottom = givenVisible.left = givenVisible.top
                    = givenVisible.right = givenVisible.bottom = 0;
            attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
            Rect contentInsets = insets.contentInsets;
            Rect visibleInsets = insets.visibleInsets;
            if (mTranslator != null) {
                contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
                visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
            }
            if (insetsPending || !mLastGivenInsets.equals(insets)) {
                mLastGivenInsets.set(insets);
                try {
                    sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
                            contentInsets, visibleInsets);
                } catch (RemoteException e) {
                }
            }
        }
        /**
         * 8 如果mFirst为 true,则让该视图获得焦点,即调用mView.requestFocus()
         */
        if (mFirst) {
            // handle first focus request
            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
                    + mView.hasFocus());
            if (mView != null) {
                if (!mView.hasFocus()) {
                    mView.requestFocus(View.FOCUS_FORWARD);
                    mFocusedView = mRealFocusedView = mView.findFocus();
                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
                            + mFocusedView);
                } else {
                    mRealFocusedView = mView.findFocus();
                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
                            + mRealFocusedView);
                }
            }
        }

        mFirst = false;
        mWillDrawSoon = false;
        mNewSurfaceNeeded = false;
        mViewVisibility = viewVisibility;
        /**
         * 如 果 mAttachlnfo.mHasWindowFocus为 true,则意味
         * 着该窗口已经是当前交互窗口,然后接着
         * 调 用 WM.LP.mayUseInputMethod()判断
         * 当前窗口是否会使用输入法,应用程序可以在
         * AndroidManifest.xml指定是否应用窗口不
         * 使用输入法,默认情况下都使用。如果是的话,则调用
         * imm.onWindowFocus()通 知 InputMethodManager有了
         * 新的窗口及视图,如果该视图可以获得焦点并可以
         * 编辑,则输入法窗口将会显示出来。
         */
        if (mAttachInfo.mHasWindowFocus) {
            final boolean imTarget = WindowManager.LayoutParams
                    .mayUseInputMethod(mWindowAttributes.flags);
            if (imTarget != mLastWasImTarget) {
                mLastWasImTarget = imTarget;
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null && imTarget) {
                    imm.startGettingWindowFocus(mView);
                    imm.onWindowFocus(mView, mView.findFocus(),
                            mWindowAttributes.softInputMode,
                            !mHasHadWindowFocus, mWindowAttributes.flags);
                }
            }
        }
        /**
        * 最后,就需要将所有的视图绘制到屏幕上,
        * 开始真正的draw()流程。关 于draw()函数的内部执
        * 行流程,见后面小节
        */

        /**
         * 1)判断没有取消绘制,并且不是newSurface,则调用draw()函数开始绘制。代码中使用cancelDraw
         * 变量表示是否“取消绘制”,该值来源于TreeObserver的 dispatchPreDraw()回调,应用程序可以添加一
         * 个 OnDispatchPreDraw接口对象,从而在开始绘制之前执行操作,并可以阻止重绘。newSurface变量的
         * 含义是指,ViewRoot中创建了 Surface对象,但是该对象还没有被W mS分配真正的显存,ViewRoot
         * 中是调用sWindowSession.relayoutWindow()为 该 Surface对象中分配真正的显存,在一般情况下,此处
         * 的 newSurface 都是 false。
         *
         */

        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
        if (!cancelDraw && !newSurface) {
            mFullRedrawNeeded = false;
            draw(fullRedrawNeeded);

            /**
             * 如果mFirst为true,则绘制完毕后,需要向WmS报告绘制完成,即调用
             *          * sWindowSession.finishDrawing()
             */
            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
                    || mReportNextDraw) {
                if (LOCAL_LOGV) {
                    Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
                }
                mReportNextDraw = false;
                if (mSurfaceHolder != null && mSurface.isValid()) {
                    mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
                    SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                    if (callbacks != null) {
                        for (SurfaceHolder.Callback c : callbacks) {
                            if (c instanceof SurfaceHolder.Callback2) {
                                ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                        mSurfaceHolder);
                            }
                        }
                    }
                }
                try {
                    sWindowSession.finishDrawing(mWindow);
                } catch (RemoteException e) {
                }
            }
        } else {
            /**
             * 如 果 cancelDraw或 者 newSurface条件不满足,则重新发起一次View遍历请求,以便当条件
             * 满足后继续绘制
             */
            // We were supposed to report when we are done drawing. Since we canceled the
            // draw, remember it here.
            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
                mReportNextDraw = true;
            }
            if (fullRedrawNeeded) {
                mFullRedrawNeeded = true;
            }
            // Try again
            scheduleTraversals();
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: at android.os.Parcel.readParcelableCreator(Parcel.java:3364) at android.os.Parcel.readParcelable(Parcel.java:3272) at android.view.InsetsSourceControl.<init>(InsetsSourceControl.java:71) at android.view.InsetsSourceControl$1.createFromParcel(InsetsSourceControl.java:116) at android.view.InsetsSourceControl$1.createFromParcel(InsetsSourceControl.java:114) at android.os.Parcel.readTypedObject(Parcel.java:3119) at android.os.Parcel.readTypedArray(Parcel.java:3089) at android.view.IWindowSession$Stub$Proxy.relayout(IWindowSession.java:1739) at android.view.ViewRootImpl.relayoutWindow(ViewRootImpl.java:7702) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2816) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2133) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8439) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1089) at android.view.Choreographer.doCallbacks(Choreographer.java:907) at android.view.Choreographer.doFrame(Choreographer.java:842) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1074) at android.os.Handler.handleCallback(Handler.java:967) at android.os.Handler.dispatchMessage(Handler.java:104) at android.os.Looper.loop(Looper.java:243) at android.app.ActivityThread.main(ActivityThread.java:8021) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1164)
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值