Activity界面绘制流程

在Activity启动流程中,执行生命周期中的Create流程,在onCreate()方法中通过调用setContentView()方法,完成view的创建。前面源码的分析得出这一步仅仅是将布局创建,并没有显示出来。这篇文章继续分析,创建后的布局是如何显示出来的。

我们知道在执行完Create流程后,接下来会执行Resume流程,这一步会调用ActivityThread中的handleResumeActivity()方法。

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
		String reason) {
	// If we are getting ready to gc after going to the background, well
	// we are back active so skip it.
	unscheduleGcIdler();
	mSomeActivitiesChanged = true;

	// TODO Push resumeArgs into the activity for consideration
	final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
	...
	
	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) {
			if (!a.mWindowAdded) {
				a.mWindowAdded = true;
				wm.addView(decor, l);
			} else {
				...
			}
		}

		...
	} else if (!willBeVisible) {
		...
	}

	...
	if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
		...

		r.activity.mVisibleFromServer = true;
		mNumVisibleActivities++;
		if (r.activity.mVisibleFromClient) {
			r.activity.makeVisible();
		}
	}

	...
}

首先会调用performResumeActivity()方法,这个方法最终会调用Activity中的onResume方法。
接着调用wm.addView(decor, l)方法,第一个参数decor是上面通过r.window.getDecorView()获得的,是一个DecorView,就是我们视图的根布局。
这里调用的是vm的addView()方法,省略了一部分代码,最后得到这个vm其实是一个WindowManagerImpl类的实例。在WindowManagerImpl类中找到addView()方法。

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

调用WindowManagerGlobal中的addView方法。

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ...

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        ...

        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.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

首先创建一个ViewRootImpl对象,然后调用setView方法。注意一下其中的第一个参数,就是上面在调用addView()方法时,传入的一个DecorView。进到ViewRootImpl的setView方法中。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
...

            mSoftInputMode = attrs.softInputMode;
            mWindowAttributesChanged = true;
            mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
            mAttachInfo.mRootView = view;
            mAttachInfo.mScalingRequired = mTranslator != null;
            mAttachInfo.mApplicationScale =
                    mTranslator == null ? 1.0f : mTranslator.applicationScale;
            if (panelParentView != null) {
                mAttachInfo.mPanelParentWindowToken
                        = panelParentView.getApplicationWindowToken();
            }
            mAdded = true;
            int res; /* = WindowManagerImpl.ADD_OKAY; */

            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            requestLayout();
            ...
            try {
                ...
            } catch (RemoteException e) {
                ...
            } finally {
                if (restore) {
                    attrs.restore();
                }
            }

            ...
        }
    }
}

mView = view,将DecorView赋值给了mView,接着调用了requestLayout()方法。

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

首先调用checkThread()方法。

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

这就是一直说的只能在UI线程更新界面的原因,如果当前线程不是主线程,会直接抛出异常。
之后会调用scheduleTraversals()方法。

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
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这个Runnable。

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

在run()方法中调用了doTraversal()方法。

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

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

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

调用performTraversals()方法。这个方法很长,我们需要知道在这个方法中先后调用了performMeasure、performLayout、performDraw三个方法。

private void performTraversals() {
	performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
	performLayout(lp, mWidth, mHeight);
	performDraw();
}

我们都知道每一个View的绘制流程都是measure、layout、draw三步,而且是按着以上顺序依次执行,原因就是在这里。首先调用performMeasure()方法。

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

调用mView的measure方法,这里的mView是前面提到的DecorView。但是看了代码会发现DecoView中没有重写View类的measure方法,所以我们看一下View中的measure方法。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...

    if (forceLayout || needsLayout) {
        ...

        int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // measure ourselves, this should set the measured dimension flag back
			onMeasure(widthMeasureSpec, heightMeasureSpec);
			mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
		} else {
			long value = mMeasureCache.valueAt(cacheIndex);
			// Casting a long to int drops the high 32 bits, no mask needed
			setMeasuredDimensionRaw((int) (value >> 32), (int) value);
			mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
		}

		...
	}

	...
}

调用了onMeasure方法,我们发现onMeasure()在DecorView中有被重写。现在知道了这里调用onMeasure方法开始绘制第一个View,是从根布局DecorView开始的。看一下DecorView的onMeasure方法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
    final boolean isPortrait =
            getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;

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

    boolean fixedWidth = false;
    mApplyFloatingHorizontalInsets = false;
    if (widthMode == AT_MOST) {
        final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.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 (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            if (w > 0) {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        Math.min(w, widthSize), EXACTLY);
                fixedWidth = true;
            } else {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - mFloatingInsets.left - mFloatingInsets.right,
                        AT_MOST);
                mApplyFloatingHorizontalInsets = true;
            }
        }
    }

    ...

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width = getMeasuredWidth();
    boolean measure = false;

    widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);

    ...

    // TODO: Support height?

    if (measure) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

在这个方法中发现有一行super.onMeasure(widthMeasureSpec, heightMeasureSpec)方法。因为DecorView继承自FrameLayout,进到FrameLayout的onMeasure()方法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();

    ...

    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            ...
        }
    }

    ...

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

    ...
}

通过for循环遍历子View,调用measureChildWithMargins()方法。

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    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);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

首先计算出子View的MeasureSpec,然后调用View的measure()方法,在上面分析DecorView时,我们知道了接着会在measure()方法中调用子View自己重写的onMeasure方法。
而我们知道DecorView的里面一层是一个LinearLayout,进到LinearLayout的onMeasure()方法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

这个LinearLayout的orientation是VERTICAL,所以调用measureVertical方法。同样的,遍历子View,调用measureChildBeforeLayout()方法。

void measureChildBeforeLayout(View child, int childIndex,
        int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
        int totalHeight) {
    measureChildWithMargins(child, widthMeasureSpec, totalWidth,
            heightMeasureSpec, totalHeight);
}

和FrameLayout同理,调用了ViewGroup的measureChildWithMargins方法。
接下来,就像前面的DecorView和刚刚LinearLayout的measure一样,通过View的measure方法,调用各子View自己重写的onMeasure方法完成布局的测量,而且这个测量过程是由外向内的。当调用performMeasure()方法,所有的布局都依次测量完成后,才会顺序执行performLayout()、performDraw()方法,依次完成layout和draw过程。
以上就是当启动一个Activity时,界面是如何加载及绘制的。并且通过源码分析了为什么界面的绘制流程是measure、layout、draw依次完成的。

发布了10 篇原创文章 · 获赞 19 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/songkai0825/article/details/104084029