Android Activity 布局绘制

01.Activity布局加载简介

  • Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window只是控制着界面布局文件的加载过程,那么界面布局文件的绘制流程是如何的呢?

02.handleResumeActivity

  • Android体系在执行Activity的onResume方法之前会回调ActivityThread的handleResumeActivity方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    final void handleResumeActivity(IBinder token,
    boolean clearHide, boolean isForward, boolean reallyResume) {
    ...
    if (r.window == null && !a.mFinished && willBeVisible) {
    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    l.softInputMode |= forwardBit;
    if (a.mVisibleFromClient) {
    a.mWindowAdded = true;
    wm.addView(decor, l);
    }

    // If the window has already been added, but during resume
    // we started another activity, then don't yet make the
    // window visible.
    }
    ...
    // The window is now visible if it has been added, we are not
    // simply finishing, and we are not starting another activity.
    if (!r.activity.mFinished && willBeVisible
    && r.activity.mDecor != null && !r.hideForNow) {
    if (r.newConfig != null) {
    r.tmpConfig.setTo(r.newConfig);
    if (r.overrideConfig != null) {
    r.tmpConfig.updateFrom(r.overrideConfig);
    }
    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
    + r.activityInfo.name + " with newConfig " + r.tmpConfig);
    performConfigurationChanged(r.activity, r.tmpConfig);
    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
    r.newConfig = null;
    }
    if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
    + isForward);
    WindowManager.LayoutParams l = r.window.getAttributes();
    if ((l.softInputMode
    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
    != forwardBit) {
    l.softInputMode = (l.softInputMode
    & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
    | forwardBit;
    if (r.activity.mVisibleFromClient) {
    ViewManager wm = a.getWindowManager();
    View decor = r.window.getDecorView();
    wm.updateViewLayout(decor, l);
    }
    }
    r.activity.mVisibleFromServer = true;
    mNumVisibleActivities++;
    if (r.activity.mVisibleFromClient) {
    r.activity.makeVisible();
    }
    }

    if (!r.onlyLocalRequest) {
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    if (localLOGV) Slog.v(
    TAG, "Scheduling idle handler for " + r);
    Looper.myQueue().addIdleHandler(new Idler());
    }
    r.onlyLocalRequest = false;

    // Tell the activity manager we have resumed.
    if (reallyResume) {
    try {
    ActivityManagerNative.getDefault().activityResumed(token);
    } catch (RemoteException ex) {
    }
    }
    ...
    }
    • 可以看到在在获取了Activity的Window相关参数之后执行了r.activity.makeVisible()方法,看样子这个就是Activity的显示方法
  • 这里我们来具体看一下makeVisible方法的具体实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    void makeVisible() {
    if (!mWindowAdded) {
    ViewManager wm = getWindowManager();
    wm.addView(mDecor, getWindow().getAttributes());
    mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
    }
    • 首先判断成员变量mWindowAdded是否为true,可以发现mWindowAdded成员变量只有在执行之后才能赋值为true,所以这里的代码的主要逻辑是该if分支只能执行一次。
    • 这里的ViewManager对象是通过getWindowManager()方法获取的。
  • 这个方法主要作用

    • 是将mDecor给显示到界面上

03.WindowManager作用

3.1 mWindowManager属性

  • 接着上面进行分析。

  • 来看一下getWindowManager()方法的具体实现:

    1
    2
    3
    public WindowManager getWindowManager() {
    return mWindowManager;
    }
    • 好吧,原来就是返回的Activity的mWindowManager的成员变量,那么这个mWindowManager的成员变量是什么时候赋值的呢?在Activity的attach方法方法中初始化了Activity的相关成员变量,这里也包括了mWindowManager,我们来看一下mWindowManager的赋值过程:
    1
    mWindowManager = mWindow.getWindowManager();
  • 那么这里的Window对象的mWindowManager成员变量是具体如何赋值的?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
    boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated
    || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
    if (wm == null) {
    wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
    • 可以发现mWindowManager=((WindowManagerImpl)vm).createLocalWindowManager(this)原来是在这里赋值的,所以一个Activity对应这一个新的Window,而这个Window对象内部会对应着一个新的WindowManager对象。
  • 接着往下看,那么createLoclWindowManager方法是如何实现的呢?

    1
    2
    3
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mDisplay, parentWindow);
    }
    • 原来是new出了一个WindowManagerImpl对象,所以回到我们的Activity的makeVisible方法,ViewManager获取的是一个WindowManagerImpl对象,所以Window对象内部的WindowManager对象其实都是一个WindowManagerImpl的实例,都是而且从继承关系上可以看到:WindowManagerImpl –> WindowManager –> ViewManager;

3.2 WindowManagerImpl介绍

  • 继续往下看:

    1
    wm.addView(mDecor, getWindow().getAttributes());
    • 这里的mDector成员变量,我们知道,它是Activity的界面根View,而getWindow.getAttrbutes方法是windowManager中定义的Params内部类,该内部类定义了许多的Window类型,由于这里的vm是WindowManagerImpl的实例。
  • 我们来看一下这里的addView的具体实现:

    1
    2
    3
    4
    5
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
  • 然后我们具体看一下mGlobal.addView方法,这里的mGlobal是一个WindowManagerGlobal的单例对象,WindowManagerGlobal是Window处理的工具类,那么WindowManagerGlobal的addView具体是如何实现的呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow) {
    ...
    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
    // Start watching for system property changes.
    if (mSystemPropertyUpdater == null) {
    mSystemPropertyUpdater = new Runnable() {
    @Override public void run() {
    synchronized (mLock) {
    for (int i = mRoots.size() - 1; i >= 0; --i) {
    mRoots.get(i).loadSystemProperties();
    }
    }
    }
    };
    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
    }

    int index = findViewLocked(view, false);
    if (index >= 0) {
    if (mDyingViews.contains(view)) {
    // Don't wait for MSG_DIE to make it's way through root's queue.
    mRoots.get(index).doDie();
    } else {
    throw new IllegalStateException("View " + view
    + " has already been added to the window manager.");
    }
    // The previous removeView() had not completed executing. Now it has.
    }

    // If this is a panel window, then find the window it is being
    // attached to for future reference.
    if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    final int count = mViews.size();
    for (int i = 0; i < count; i++) {
    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
    panelParentView = mViews.get(i);
    }
    }
    }

    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    }

    // do this last because it fires off messages to start doing things
    try {
    root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
    // BadTokenException or InvalidDisplayException, clean up.
    synchronized (mLock) {
    final int index = findViewLocked(view, false);
    if (index >= 0) {
    removeViewLocked(index, true);
    }
    }
    throw e;
    }
    }
  • 可以发现在WindowManagerGlobal中存在着三个数据列表:

    1
    2
    3
    4
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
    new ArrayList<WindowManager.LayoutParams>();
    • 其中mViews主要用于保存Activity的mDector也就是Activity的根View,
    • 而mRoots主要用于保存ViewRootImpl,
    • mParams主要用于保存Window的LayoutParams,WindowManagerGlobal主要作为WindowManagerImpl的辅助方法类,用于操作View组件。
  • 最后我们调用了root.setView方法,这个方法很重要我们就是在这里实现了我们的root与ViewRootImpl的关联的,除了实现了mDector与ViewRootImpl的相互关联,我们还调用了requestLayout方法,这里我们看一下setView方法的具体实现:

    1
    2
    3
    4
    5
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    requestLayout();
    ...
    }
  • 可以看到,在方法体中又调用了requestLayout方法,这个方法其实就是调用执行重绘的请求,我们来看一下这个requestLayout方法具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
    checkThread();
    mLayoutRequested = true;
    scheduleTraversals();
    }
    }
  • 可以看到这里有一个checkThread方法,这个方法是检查当前线程的方法,若当前线程非UI线程,则抛出非UI线程更新UI的错误:

    1
    2
    3
    4
    5
    6
    void checkThread() {
    if (mThread != Thread.currentThread()) {
    throw new CalledFromWrongThreadException(
    "Only the original thread that created a view hierarchy can touch its views.");
    }
    }
    • 相信大家平时在编程的过程中肯定会遇到过这个错误,ViewRootImpl是具体更新View的管理类,所有关于View的更新操作都是在这里执行的,自然而然的对于更新线程的检测是在这个类中添加的,一般在更新UI的时候都会调用这个方法用于检测当前执行更新UI的线程是否是UI线程,否则就会抛出这个异常。

04.requestLayout介绍

4.1 requestLayout方法

  • 继续回到我们的requestLayout方法,这里又调用了scheduleTraversales方法,我们来看一下这个方法的具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
    checkThread();
    mLayoutRequested = true;
    scheduleTraversals();
    }
    }

4.2 scheduleTraversals()

  • 然后看scheduleTraversales方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void scheduleTraversals() {
    if (!mTraversalScheduled) {
    mTraversalScheduled = true;
    mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
    mChoreographer.postCallback(
    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    if (!mUnbufferedInputDispatch) {
    scheduleConsumeBatchedInput();
    }
    notifyRendererOfFramePending();
    pokeDrawLockIfNeeded();
    }
    }
    • 这里mChoreographer.postCallback,内部会调用一个异步消息,用于执行mTraversalRunnable的run方法,这个mTraversalRunnable是一个Runnable对象
  • 我们来看一下mTraversalRunnable类的定义:

    1
    2
    3
    4
    5
    6
    final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
    doTraversal();
    }
    }
  • 在TraversalRunnable类的run方法中调用了doTraversal方法,我们来看一下这个方法的具体实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    void doTraversal() {
    if (mTraversalScheduled) {
    mTraversalScheduled = false;
    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

    if (mProfile) {
    Debug.startMethodTracing("ViewAncestor");
    }

    performTraversals();

    if (mProfile) {
    Debug.stopMethodTracing();
    mProfile = false;
    }
    }
    }
    • 好吧,其内部又回调了方法performTraversals方法

05.performTraversals()方法

  • 这个方法就是整个View的绘制起始方法,从这个方法开始我们的View经过大小测量,位置测量,界面绘制三个逻辑操作之后就可以展示在界面中了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    private void performTraversals() {
    ...
    // 执行View组件的onMeasure方法,主要用于测量View
    if (!mStopped || mReportNextDraw) {
    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);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

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

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

    // Implementation of weights from WindowManager.LayoutParams
    // We just grow the dimensions as needed and re-measure if
    // needs be
    int width = host.getMeasuredWidth();
    int height = host.getMeasuredHeight();
    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);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    layoutRequested = true;
    }
    }
    }
    ...
    // 主要用于测量View组件的位置
    ...
    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    boolean triggerGlobalLayoutListener = didLayout
    || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);

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

    if ((host.mPrivateFlags & View.PFLAG_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);
    mFullRedrawNeeded = true;
    // reconfigure window manager
    try {
    mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
    } catch (RemoteException e) {
    }
    }
    }

    if (DBG) {
    System.out.println("======================================");
    System.out.println("performTraversals -- after setFrame");
    host.debug();
    }
    }
    ...
    // 主要用于View的绘制过程
    ...
    if (!cancelDraw && !newSurface) {
    if (!skipDraw || mReportNextDraw) {
    if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
    for (int i = 0; i < mPendingTransitions.size(); ++i) {
    mPendingTransitions.get(i).startChangingAnimations();
    }
    mPendingTransitions.clear();
    }

    performDraw();
    }
    } else {
    if (viewVisibility == View.VISIBLE) {
    // Try again
    scheduleTraversals();
    } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
    for (int i = 0; i < mPendingTransitions.size(); ++i) {
    mPendingTransitions.get(i).endChangingAnimations();
    }
    mPendingTransitions.clear();
    }
    }

    mIsInTraversal = false;
    }
  • 可以看到在方法performTraversals方法,我们调用了performMeasure,performLayout,performDraw三个方法,这几个方法主要用于测量View组件的大小,测量View组件的位置,绘制View组件;

    • 即:测量大小 –> 测量位置 –> 绘制组件

5.1 performMeasure测量

  • 好吧,这里我们调用了performMeasure方法,我们先看一下performMeasure方法的具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    }
  • 可以看到在performMeasure方法中我们又调用了mView的measure方法,这里的mView就是我们一开始的Activity的mDector根组件,这里的measure方法就是调用的mDector组件的measure方法:

    1
    2
    3
    4
    5
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
    }
  • 在View的measure方法中,又调用了onMeasure方法,由于我们的mDector对象是一个FrameLayout,所以这里的onMeasure执行的是FrameLayout的onMeasure方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();

    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++) {
    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());
    }

    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
    resolveSizeAndState(maxHeight, heightMeasureSpec,
    childState << MEASURED_HEIGHT_STATE_SHIFT));

    count = mMatchParentChildren.size();
    if (count > 1) {
    for (int i = 0; i < count; i++) {
    final View child = mMatchParentChildren.get(i);
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    final int childWidthMeasureSpec;
    if (lp.width == LayoutParams.MATCH_PARENT) {
    final int width = Math.max(0, getMeasuredWidth()
    - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
    - lp.leftMargin - lp.rightMargin);
    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
    width, MeasureSpec.EXACTLY);
    } else {
    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
    getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
    lp.leftMargin + lp.rightMargin,
    lp.width);
    }

    final int childHeightMeasureSpec;
    if (lp.height == LayoutParams.MATCH_PARENT) {
    final int height = Math.max(0, getMeasuredHeight()
    - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
    - lp.topMargin - lp.bottomMargin);
    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
    height, MeasureSpec.EXACTLY);
    } else {
    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
    getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
    lp.topMargin + lp.bottomMargin,
    lp.height);
    }

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    }
    }
  • 可以看到这里调用了一个循环逻辑,获取该View的所有子View,并执行所有子View的measure方法,这样又回到View的measure方法,这样经过一系列的循环遍历过程,如果是ViewGroup就会调用其ViewGroup的onMeasure方法,若果是View组件就会调用View的onMeasure方法,我们来看一下View的onMeasure方法:

    1
    2
    3
    4
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
  • 可以看到这个方法中调用了setMeasuredDimension方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    }
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }
  • 好吧,方法体里面又调用了setMeasuredDimensionRaw方法:

    1
    2
    3
    4
    5
    6
    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;

    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }
  • 这样把View组件即其子View的大小测量出来了,并且保存在了成员变量mMeasuredWith和mMeasuredHeight中。

5.2 performLayout布局

  • 继续回到我们的performTransles方法,然后我们继续看performLayout方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
    int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    mInLayout = true;

    final View host = mView;
    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
    Log.v(TAG, "Laying out " + host + " to (" +
    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
    }

    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

    mInLayout = false;
    int numViewsRequestingLayout = mLayoutRequesters.size();
    if (numViewsRequestingLayout > 0) {
    // requestLayout() was called during layout.
    // If no layout-request flags are set on the requesting views, there is no problem.
    // If some requests are still pending, then we need to clear those flags and do
    // a full request/measure/layout pass to handle this situation.
    ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
    false);
    if (validLayoutRequesters != null) {
    // Set this flag to indicate that any further requests are happening during
    // the second pass, which may result in posting those requests to the next
    // frame instead
    mHandlingLayoutInLayoutRequest = true;

    // Process fresh layout requests, then measure and layout
    int numValidRequests = validLayoutRequesters.size();
    for (int i = 0; i < numValidRequests; ++i) {
    final View view = validLayoutRequesters.get(i);
    Log.w("View", "requestLayout() improperly called by " + view +
    " during layout: running second layout pass");
    view.requestLayout();
    }
    measureHierarchy(host, lp, mView.getContext().getResources(),
    desiredWindowWidth, desiredWindowHeight);
    mInLayout = true;
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

    mHandlingLayoutInLayoutRequest = false;

    // Check the valid requests again, this time without checking/clearing the
    // layout flags, since requests happening during the second pass get noop'd
    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
    if (validLayoutRequesters != null) {
    final ArrayList<View> finalRequesters = validLayoutRequesters;
    // Post second-pass requests to the next frame
    getRunQueue().post(new Runnable() {
    @Override
    public void run() {
    int numValidRequests = finalRequesters.size();
    for (int i = 0; i < numValidRequests; ++i) {
    final View view = finalRequesters.get(i);
    Log.w("View", "requestLayout() improperly called by " + view +
    " during second layout pass: posting in next frame");
    view.requestLayout();
    }
    }
    });
    }
    }

    }
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
    }
  • 可以看到在方法体中,我们看到该方法执行了layout方法,我们看一下该layout方法的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
    onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
    setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
    onLayout(changed, l, t, r, b);
    mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnLayoutChangeListeners != null) {
    ArrayList<OnLayoutChangeListener> listenersCopy =
    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
    int numListeners = listenersCopy.size();
    for (int i = 0; i < numListeners; ++i) {
    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
    }
    }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
  • 可以看到这个方法体中执行了onLayout方法,这个方法就是具体执行测量位置的方法了,由于我们的mDector是一个FrameLayout,所以跟measure类似的,我们看一下FrameLayout的onLayout方法的实现:

    1
    2
    3
    4
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }
    • 可以看到这里调用了layoutChildren方法,让我们来看一下layoutChildren方法的实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    void layoutChildren(int left, int top, int right, int bottom,
    boolean forceLeftGravity) {
    final int count = getChildCount();

    final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();

    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();

    for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (child.getVisibility() != GONE) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();

    final int width = child.getMeasuredWidth();
    final int height = child.getMeasuredHeight();

    int childLeft;
    int childTop;

    int gravity = lp.gravity;
    if (gravity == -1) {
    gravity = DEFAULT_CHILD_GRAVITY;
    }

    final int layoutDirection = getLayoutDirection();
    final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
    final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

    switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    case Gravity.CENTER_HORIZONTAL:
    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
    lp.leftMargin - lp.rightMargin;
    break;
    case Gravity.RIGHT:
    if (!forceLeftGravity) {
    childLeft = parentRight - width - lp.rightMargin;
    break;
    }
    case Gravity.LEFT:
    default:
    childLeft = parentLeft + lp.leftMargin;
    }

    switch (verticalGravity) {
    case Gravity.TOP:
    childTop = parentTop + lp.topMargin;
    break;
    case Gravity.CENTER_VERTICAL:
    childTop = parentTop + (parentBottom - parentTop - height) / 2 +
    lp.topMargin - lp.bottomMargin;
    break;
    case Gravity.BOTTOM:
    childTop = parentBottom - height - lp.bottomMargin;
    break;
    default:
    childTop = parentTop + lp.topMargin;
    }

    child.layout(childLeft, childTop, childLeft + width, childTop + height);
    }
    }
    }
  • 跟measure类似的,这里也是遍历执行View的layout方法,若是ViewGroup则执行具体的ViewGroup的layout方法,若是View,则执行View的layout方法,好吧,我们看一下View的layout的具体实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
    onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
    setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
    onLayout(changed, l, t, r, b);
    mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnLayoutChangeListeners != null) {
    ArrayList<OnLayoutChangeListener> listenersCopy =
    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
    int numListeners = listenersCopy.size();
    for (int i = 0; i < numListeners; ++i) {
    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
    }
    }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
    • 这样经过layout方法,如果是View组件的话就已经将View组件的位置信息计算出来并保存在对象的成员变量中。

5.3 performDraw绘制

  • 经过了测量大小与测量位置的逻辑之后,最后看一下performTraversals方法中的performDraw方法,这个方法的作用就是执行View组件的绘制逻辑了。

    1
    2
    3
    4
    5
    private void performDraw() {
    ...
    draw(fullRedrawNeeded);
    ...
    }
  • 可以看到这里调用了ViewRootImpl的draw方法,然后我们看一下draw方法的实现:

    1
    2
    3
    4
    5
    6
    7
    private void draw(boolean fullRedrawNeeded) {
    ...
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
    return;
    }
    ...
    }
  • 可以看到这里又调用了drawSoftware方法,看名字这里应该就是调用执行绘制的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
    boolean scalingRequired, Rect dirty) {

    ...
    mView.draw(canvas);
    ...
    return true;
    }
  • 可以看到这里调用了mView的draw方法,这里的mView是我们的mDector,好吧,看一下draw方法的具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
    (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /*
    * Draw traversal performs several drawing steps which must be executed
    * in the appropriate order:
    *
    * 1. Draw the background
    * 2. If necessary, save the canvas' layers to prepare for fading
    * 3. Draw view's content
    * 4. Draw children
    * 5. If necessary, draw the fading edges and restore layers
    * 6. Draw decorations (scrollbars for instance)
    */

    // Step 1, draw the background, if needed
    int saveCount;

    if (!dirtyOpaque) {
    drawBackground(canvas);
    }

    // skip step 2 & 5 if possible (common case)
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
    // Step 3, draw the content
    if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
    dispatchDraw(canvas);

    // Overlay is part of the content and draws beneath Foreground
    if (mOverlay != null && !mOverlay.isEmpty()) {
    mOverlay.getOverlayView().dispatchDraw(canvas);
    }

    // Step 6, draw decorations (foreground, scrollbars)
    onDrawForeground(canvas);

    // we're done...
    return;
    }

    /*
    * Here we do the full fledged routine...
    * (this is an uncommon case where speed matters less,
    * this is why we repeat some of the tests that have been
    * done above)
    */

    boolean drawTop = false;
    boolean drawBottom = false;
    boolean drawLeft = false;
    boolean drawRight = false;

    float topFadeStrength = 0.0f;
    float bottomFadeStrength = 0.0f;
    float leftFadeStrength = 0.0f;
    float rightFadeStrength = 0.0f;

    // Step 2, save the canvas' layers
    int paddingLeft = mPaddingLeft;

    final boolean offsetRequired = isPaddingOffsetRequired();
    if (offsetRequired) {
    paddingLeft += getLeftPaddingOffset();
    }

    int left = mScrollX + paddingLeft;
    int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
    int top = mScrollY + getFadeTop(offsetRequired);
    int bottom = top + getFadeHeight(offsetRequired);

    if (offsetRequired) {
    right += getRightPaddingOffset();
    bottom += getBottomPaddingOffset();
    }

    final ScrollabilityCache scrollabilityCache = mScrollCache;
    final float fadeHeight = scrollabilityCache.fadingEdgeLength;
    int length = (int) fadeHeight;

    // clip the fade length if top and bottom fades overlap
    // overlapping fades produce odd-looking artifacts
    if (verticalEdges && (top + length > bottom - length)) {
    length = (bottom - top) / 2;
    }

    // also clip horizontal fades if necessary
    if (horizontalEdges && (left + length > right - length)) {
    length = (right - left) / 2;
    }

    if (verticalEdges) {
    topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
    drawTop = topFadeStrength * fadeHeight > 1.0f;
    bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
    drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
    }

    if (horizontalEdges) {
    leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
    drawLeft = leftFadeStrength * fadeHeight > 1.0f;
    rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
    drawRight = rightFadeStrength * fadeHeight > 1.0f;
    }

    saveCount = canvas.getSaveCount();

    int solidColor = getSolidColor();
    if (solidColor == 0) {
    final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

    if (drawTop) {
    canvas.saveLayer(left, top, right, top + length, null, flags);
    }

    if (drawBottom) {
    canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
    }

    if (drawLeft) {
    canvas.saveLayer(left, top, left + length, bottom, null, flags);
    }

    if (drawRight) {
    canvas.saveLayer(right - length, top, right, bottom, null, flags);
    }
    } else {
    scrollabilityCache.setFadeColor(solidColor);
    }

    // Step 3, draw the content
    if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
    dispatchDraw(canvas);

    // Step 5, draw the fade effect and restore layers
    final Paint p = scrollabilityCache.paint;
    final Matrix matrix = scrollabilityCache.matrix;
    final Shader fade = scrollabilityCache.shader;

    if (drawTop) {
    matrix.setScale(1, fadeHeight * topFadeStrength);
    matrix.postTranslate(left, top);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(left, top, right, top + length, p);
    }

    if (drawBottom) {
    matrix.setScale(1, fadeHeight * bottomFadeStrength);
    matrix.postRotate(180);
    matrix.postTranslate(left, bottom);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(left, bottom - length, right, bottom, p);
    }

    if (drawLeft) {
    matrix.setScale(1, fadeHeight * leftFadeStrength);
    matrix.postRotate(-90);
    matrix.postTranslate(left, top);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(left, top, left + length, bottom, p);
    }

    if (drawRight) {
    matrix.setScale(1, fadeHeight * rightFadeStrength);
    matrix.postRotate(90);
    matrix.postTranslate(right, top);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(right - length, top, right, bottom, p);
    }

    canvas.restoreToCount(saveCount);

    // Overlay is part of the content and draws beneath Foreground
    if (mOverlay != null && !mOverlay.isEmpty()) {
    mOverlay.getOverlayView().dispatchDraw(canvas);
    }

    // Step 6, draw decorations (foreground, scrollbars)
    onDrawForeground(canvas);
    }
  • 整个View的绘制流程还是比较清楚的,整个执行逻辑还有相应的注释,并且在执行draw方法的过程中,如果包含子View,那么也会执行子View的draw方法,好吧,经过这样一系列的执行逻辑之后,mDector以及子View就被绘制出来了。

06.Activity布局绘制总结

  • 总结如下所示:
    • Activity执行onResume之后再ActivityThread中执行Activity的makeVisible方法。
    • View的绘制流程包含了测量大小,测量位置,绘制三个流程;
    • Activty的界面绘制是从mDector即根View开始的,也就是从mDector的测量大小,测量位置,绘制三个流程;
    • View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;
    • View的测量大小流程:performMeasure –> measure –> onMeasure等方法;
    • View的测量位置流程:performLayout –> layout –> onLayout等方法;
    • View的绘制流程:onDraw等方法;
    • View组件的绘制流程会在onMeasure,onLayout以及onDraw方法中执行分发逻辑,也就是在onMeasure同时执行子View的测量大小逻辑,在onLayout中同时执行子View的测量位置逻辑,在onDraw中同时执行子View的绘制逻辑;
    • Activity中都对应这个一个Window对象,而每一个Window对象都对应着一个新的WindowManager对象(WindowManagerImpl实例);

07.Activity布局绘制流程图

  • 如图所示
    • img
  • View的绘制是从上往下一层层迭代下来的。DecorView–>ViewGroup(—>ViewGroup)–>View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制)。
    • img