Activity启动----setView之后(View的绘制过程)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity启动----setView之后(View的绘制过程)相关的知识,希望对你有一定的参考价值。

上一篇文章最后结束在RootViewImpl.setView()函数,这个函数之后发生了什么事情,我们接着分析。

技术分享

 1. RootViewImpl简介

ViewRootImpl作为视图层次中的顶层,实现了View和WindowManager之间需要的协议,与SystemServer进程的WindowManagerService有交互,具体实现了WindowManagerGlobal内部的大部分功能。

技术分享

1.1 ViewRootImpl的定义:

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ......
    final IWindowSession mWindowSession;
    final W mWindow;                //W继承于IWindow.Stub类。
    View mView;                     // 由setView()函数赋值。
    private final Surface mSurface = new Surface();       //mSurface可能存在多线程访问,需要加锁保护。
    ......
    public ViewRootImpl(Context context, Display display) {
        ......
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mWindow = new W(this);
        ......
    }
}

mWindowSession将被用于建立Activity的ViewRootImpl和WindowManagerService的关系。创建代码如下:

getWindowSession()@WindowManagerGlobal.java

public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager imm = InputMethodManager.getInstance();
                IWindowManager windowManager = getWindowManagerService(); //得到WindowMangerService的Binder代理.
                sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());  ///openSession操作是一个使用Binder通信的跨进程调用。
                ...... } catch (RemoteException e) { ...... } } return sWindowSession; } }

getWindowManagerService()@WindowManagerGlobal.java

public static IWindowManager getWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowManagerService == null) {
            sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
        }
        return sWindowManagerService;
    }
}

openSession()@WindowManagerService.java

public IWindowSession openSession(IInputMethodClient client,
            IInputContext inputContext) {
    ......
    Session session = new Session(this, client, inputContext);
    return session;
}

可以看到,通过WindowManagerService.openSession()获得一个Session对象。类Session继承于IWindowSession.Stub,并实现了IBinder接口。它支持Binder通信,并属于Bn端,即响应请求的服务端。

1.2 ViewRootImpl与WindowManagerService的关系

    ViewRootImpl通过mWindowSession(IWindowSession)与WMS进程进行跨进程通信。

    ViewRootImpl的mWindow是基于Binder通信的类,而且W是IWindow的Bn端,用于响应请求。

    它们之间属于跨进程通信,关系如下图:

   技术分享

    每个APP进程都会和WMS建立一个IWindowSession会话,APP进程利用这个会话与WMS通信。WMS利用IWindow进行事件通知,每当发生一些事件(按键触摸事件)时,WMS就会告诉某个IWindow.

    事件分发大致流程:

    (1) WMS所在的SystemServer进程接收到按键事件。

    (2) WMS找到UI位于屏幕顶端的进程所对应的IWindow对象,这个是Bp端对象。

    (3) 调用这个IWindow对象的dispatchKey。 IWindow对象的Bn端位于ViewRootImpl中,ViewRootImpl根据内部View的位置信息找到真正处理这个事件的View,最后调用dispatchKey函数完成事件处理。

    每个按键事件都会被转化成一个消息,由系统将这个消息加入到对应进程的消息队列中。该进程在处理消息派发时,根据消息的句柄找到对应的Window,进而由Window处理掉这个消息。

2. RootViewImpl.setView()函数

setView()@RootViewImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;                 //传进来的view是PhoneWindow的DecorView,保存起来。
            mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
            mFallbackEventHandler.setView(view);
            mWindowAttributes.copyFrom(attrs);
            ......
            mSoftInputMode = attrs.softInputMode;         // InputMode可以影响输入法弹出时视图应该怎么显示。
            ......
            mAdded = true;
            int res;     /* = WindowManagerImpl.ADD_OKAY; */
            //在被加到WindowManager之前调度第一次layout,确保收到系统事件之前重新进行了布局。
            requestLayout();

            if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();          //创建InputChannel对象
            }
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,            //注意第一个参数是mWindow. 继承IWindow.Stub类。
                             getHostVisibility(), mDisplay.getDisplayId(),
                             mAttachInfo.mContentInsets, mInputChannel);
            } catch (RemoteException e) {
                ......
            }

           // 创建InputQueue和WindowInputEventReceiver与窗口输入事件相关的对象。 if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; // 根据res设置touch mode。 mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; // 根据res设置visible属性。 ...... } } }

3. performTraversals()函数

requestLayout()函数的功能就是触发UI绘制。结合时序图。调用3至调用15,是消息发送传递及Callback设置过程。最后会由TraversalRunnable的run函数中调用RootViewImpl.doTraversal()函数去调用performTraversals()开始进行UI绘制。

performTraversals()函数很复杂,我们先专注于时序图展示的内容。

performTraversals()@RootViewImpl.java

private void performTraversals() {
    ......
    final View host = mView;

    if (mFirst || windowShouldResize || insetsChanged ||
              viewVisibilityChanged || params != null) {
......

          try {
              ......
              relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
          }
          ......

        if (!mStopped) {
            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                            (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);       //获取测量模式。lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                // Ask host how big it wants to be
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);            // 根据测量模式进行测量。
                ......
                layoutRequested
= true; } } } final boolean didLayout = layoutRequested && !mStopped; boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; if (didLayout) { performLayout(lp, desiredWindowWidth, desiredWindowHeight); // By this point all views have been sized and positioned         ...... }     ......      boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() || viewVisibility != View.VISIBLE; if (!cancelDraw && !newSurface) { if (!skipDraw || mReportNextDraw) {             ......
            performDraw(); } }
    ...... }

3.1 relayoutWindow()函数

relayoutWindow()@ViewRootImpl.java

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                           boolean insetsPending) throws RemoteException {
    ......
    // 调用WMS的relayout函数。通过该函数修改一个window的参数,提供新的参数,它将返回屏幕上window的新的一帧(忽略位置),和该window的surface。如果window当前是隐藏的,那么surface是无效的,否则就可以使用它来画window的内容。
    int relayoutResult = mWindowSession.relayout(
                             mWindow,  // 需要修改参数的window
                             mSeq,     // 排序的序列号
                             params,   // 要应用到window的新参数
                             (int) (mView.getMeasuredWidth() * appScale + 0.5f), //window想要的宽
                             (int) (mView.getMeasuredHeight() * appScale + 0.5f), //window想要的高
                             viewVisibility, // window的root view的可见性
                             insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                             mWinFrame, // outFrame, 放着屏幕上新position/size的Rect
                             mPendingOverscanInsets,
                             mPendingContentInsets,
                             mPendingVisibleInsets,
                             mPendingConfiguration,
                             mSurface  // 存放新显示surface的对象。
                         );
    ......
    return relayoutResult;
}

3.2 performMeasure()

    在执行performMesure()时,需要传入测量模式,这里简单介绍下MeasureSpec。MeasureSpec封装了从父view传 给子view的布局要求,每个MeasureSpec代表对宽度或高度的要求。每个MeasureSpec值由大小和模式组成,高16位表示mode,低 16位表示size,即父view的size。
MeasureSpec有三种可能的模式:
    UNSPECIFIED: 父view没有强加任何限制给子view,子view可以有它想要的size。
    EXACTLY:父view决定子view的确切size,子view将被指定这些边界,而忽视它想要的size.
    AT_MOST: 父view对子view没有限制,子view可以获得它自己指定的size。

对于DecorView来说,mode一般为EXACTLY,size为屏幕的宽高。

看下父view中是如何得到MeasureSpec的:

getRootMeasureSpec()@ViewRootImpl.java

// 根据layout params计算window中root view的MeasureSpec.
// windowSize: window可用的宽或高
// window的宽或高的layout params.
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {
        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can‘t resize. Force root view to be windowSize.
            // MeasureSpec.EXACTLY: 父控件决定子控件的确切大小,子控件与父控件一样大,子控件要求的大小被忽视。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            // MeasureSpec.AT_MOST:子控件的大小可以为它指定的大小。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
    }
    return measureSpec;
}

performMeasure()@ViewRootImpl.java

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
         mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);     // 调用mView的measure。
    } finally {
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

measure()@View.java 

// 这个函数计算出view的大小,然后设置实际的宽和高。每个view的实际宽高都是由父view和自身共同决定的。
// 实际测量工作在onMeasure()函数中执行,子类也能重载onMesure()函数实现对自身的测量。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  // final方法,子类不可以重写
    ......if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
            widthMeasureSpec != mOldWidthMeasureSpec ||
            heightMeasureSpec != mOldHeightMeasureSpec) {

        // first clears the measured dimension flag
        mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

        resolveRtlPropertiesIfNeeded();

        int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
                mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // 调用onMeasure方法, 测量自己,这个调用需要重新设置measured dimension flag。
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        ......
        // flag not set, setMeasuredDimension() was not invoked, we raise
        // an exception to warn the developer
        if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
            // 子类重写onMeausre方法的时,必须调用setMeasuredDimension或者super.onMeasure方法,不然就会走到这里报错。
            // setMeasuredDimension中回去改变mPrivateFlags的值
            throw new IllegalStateException("onMeasure() did not set the"
                    + " measured dimension by calling"
                    + " setMeasuredDimension()");
        }

        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    }

    mOldWidthMeasureSpec = widthMeasureSpec;
    mOldHeightMeasureSpec = heightMeasureSpec;

    mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
            (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}

onMeasure()@View.java

// 提供view和它的内容的测量宽高,应该由子类重写这个函数来提供准确有效的测量值。这个函数的两个参数都是由父view传过来的,代表父view的规格。
// 子类重写这个函数的时候必须调用setMeasuredDimension()保存这个view的测量宽和高,确保测量的宽和高至少是view的最小宽高。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                         getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

setMeasuredDimension()@View.java

// 设置测量结果及flags.
protected
final void setMeasuredDimension(int measuredWidth, int measuredHeight) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } // 赋值给mMeasuredWidth,getMeasuredWidth()函数返回的就是这个值。
    // measure的目的就是对view树中的每个view的mMeasuredWidth和mMesuredHeight赋值。这两个值被赋值,该view的测量工作才算结束。
mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; // 设置flag, 这就是重写onMeasure方法时如果不调用setMeasuredDimension方法时, measure会抛出异常的原因。 mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }

View,ViewGroup,FrameLayout,DecorView的关系如下:

技术分享

DecorView.onMesure()@PhoneWindow.java

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
    final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;

    final int widthMode = getMode(widthMeasureSpec);
    final int heightMode = getMode(heightMeasureSpec);

    boolean fixedWidth = false;
    if (widthMode == AT_MOST) {
        final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
        if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
            final int w;
            if (tvw.type == TypedValue.TYPE_DIMENSION) {
                w = (int) tvw.getDimension(metrics);
            } else if (tvw.type == TypedValue.TYPE_FRACTION) {
                w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
            } else {
                w = 0;
            }
            if (w > 0) {
                final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(w, widthSize), EXACTLY);
                fixedWidth = true;
            }
        }
    }

    if (heightMode == AT_MOST) {
        final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
        if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
            final int h;
            if (tvh.type == TypedValue.TYPE_DIMENSION) {
                h = (int) tvh.getDimension(metrics);
            } else if (tvh.type == TypedValue.TYPE_FRACTION) {
                h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
            } else {
                h = 0;
            }
            if (h > 0) {
                final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(h, heightSize), EXACTLY);
            }
        }
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    // 调用FrameLayout.onMeasure().
    int width = getMeasuredWidth(); boolean measure = false; widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); if (!fixedWidth && widthMode == AT_MOST) { final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; if (tv.type != TypedValue.TYPE_NULL) { final int min; if (tv.type == TypedValue.TYPE_DIMENSION) { min = (int)tv.getDimension(metrics); } else if (tv.type == TypedValue.TYPE_FRACTION) { min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); } else { min = 0; } if (width < min) { widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); measure = true; } } } if (measure) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }

 onMesure()@FrameLayout.java

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();         // 获得子view的数目

    final boolean measureMatchParentChildren =
               MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
               MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    mMatchParentChildren.clear();

    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;

    for (int i = 0; i < count; i++) {    // 测量每个子View
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
            maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
            childState = combineMeasuredStates(childState, child.getMeasuredState());
            if (measureMatchParentChildren) {
                if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
                    mMatchParentChildren.add(child);
                }
            }
        }
    }

    // Account for padding too
    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
 
    // Check against our minimum height and width
    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // Check against our foreground‘s minimum height and width
    final Drawable drawable = getForeground();
    if (drawable != null) {
        maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
        maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }

    // 设置自己的size, 即ViewGroup的size. 可以看到,它的size由父view及子view共同决定。 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState
<< MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) {   // 对于match parent的child进行测量。 for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }

 measureChildWithMargins()@ViewGroup.java

// 要求view的一个child测量自己,同时考虑MeasureSpec要求和padding和margins.
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
    int parentHeightMeasureSpec, int heightUsed) {
    // 获取子view的LayoutParams
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();     // 调整MeasureSpec,通过传进来的父view的MeasureSpec及子view自身的LayoutParams,共同决定子view的MeasureSpec. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);      // 调用子view的measure(),又重新开始上面measure的流程。子view的measure()中会回调子view的onMeasure()函数。 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }

getChildMeasureSpec()@ViewGroup.java

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //获取parent view的mode和size
   int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec);     //parent size与padding的差值(即parent中除去padding后剩余的大小),若小于0则直接返回0. int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) {  //根据parent mode进行处理 // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) {                     // 若child的layout_width或者layout_height在xml或者代码中指定了大于等于0的值,
               // 则设置child的size值为指定的值,mode为EXACTLY.
                resultSize
= childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. // 若child的layout_width或者layout_height在xml或者代码中指定为MATCH_PARENT,
               // 则设置child的size值为size,mode为EXACTLY.

                resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can‘t be bigger than us. // 若child的layout_width或者layout_height在xml或者代码中指定为WRAP_CONTENT,
               // 则设置child的size值为指定的值,mode为AT_MOST.

                resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us // 下面两个分支类似
        case MeasureSpec.AT_MOST:            ......break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED:             ......break; }
    //使用mode和size生成MeasureSpec返回。
return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }

   可以看到,getChildMeasureSpec()是通过其父view提供的MeasureSpec参数得到specMode和specSize,然后根据计算出来的specMode以及子view的childDimension(layout_width或layout_height)来计算自身的measureSpec,如果其本身包含子视图,则计算出来的measureSpec将作为调用其子视图measure函数的参数,同时也作为自身调用setMeasuredDimension()的参数,如果其不包含子视图则默认情况下最终会调用onMeasure的默认实现,并最终调用到setMeasuredDimension()。

performMeasure()小结:

(1) mView.measure() , 即调用View.measure()方法。

(2) View.measure()内部调用onMeasure()方法,因为这里的mView是DecorView,调用的是DecorView.onMeasure(), 这个函数内部调用super.onMeasure()。因为DecorView继承于FrameLayout,所以super.onMeasure()即调用FrameLayout的onMeasure()方法。

(3) FrameLayout.onMeasure()对每个子view进行遍历测量,对每个子view调用measureChildWithMargins()方法测量子view。

(4) measureChildWithMargins()内部调用getChildMeasureSpec()计算出传给子view的MeasureSpec,并调用子view的measure()方法。

(5) FrameLayout.onMeasure()中根据父view及(3)测量出来的子view的MeasureSpec信息调用setMeasuredDimension()设置自身的size。

(6) 使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取view测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

(7) ViewGroup的子类必须要求LayoutParams继承于MarginLayoutParams,否则无法使用layout_margin参数。

3.3 performLayout()

performLayout()@ViewRootImpl.java

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    mInLayout = true;
 
    final View host = mView;
 
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 把performMeasure测量的结果传入layout()函数
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
 
        mInLayout = false;
        ......
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

这个函数内调用mView.layout()方法,而ViewGroup中重写了layout()方法,并调用了super.layout(), 即View.layout()。
layout()@View.java

// 给view及它的子孙的size及position赋值。
// 这是布局机制的第二阶段(第一阶段是measuring)。在这个阶段,每个父view都会调用其所有子view的layout()来放置它们。
// 子类中不应该重载这个方法,若子类中有children, 则该子类应该重载onLayout()函数,在这个函数中调用每个child的layout.
public void layout(int l, int t, int r, int b) {
    ......int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;
 
    // 这部分是判断这个View的大小是否已经发生了变化,来判断是否需要重新layout。判断的方式就是通过将l,t,r,b参数传给setFrame()赋值给
    // mLeft,mTop, mRight,mBottom。
// isLayoutModeOptical(): 如果mParent是一个使用可见范围进行编排的ViewGroup, 返回true. boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { // 调用onLayout方法 onLayout(changed, l, t, r, b); mPrivateFlags &= ~Android查缺补漏--Activity生命周期和启动模式

appium如何获取到APP的启动activity

Android View绘制流程

Android View绘制流程

在应用程序完成获取并将获取的数据传递给它之后,我应该如何启动一个新的 Activity?

Activity的四种启动模式