在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依次完成的。