問題:為什么在Activity的onCreate()、onStart()缚甩、onResume()方法中直接通過 View.getHeight() 和 View.getMeasuredHeight() 方法都拿不到View的寬高呢饺蚊?
首先我們來看Activity的onResume是在什么時候被調(diào)用的讨便,在ActivityThread.java 中執(zhí)行handleResumeActivity()方法
/***
*在ActivityThread.java 中
*/
@Override
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
//performResumeActivity執(zhí)行Activity的onResume()方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); //注釋 Ⅰ
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
final Activity a = r.activity;
final int forwardBit = isForward
? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
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 (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//開始添加 view 磕蛇,在wm.addView()方法中會真正調(diào)用view的測量命锄、布局、繪制
wm.addView(decor, l); //注釋 Ⅳ
} else {
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
//省略部分代碼...
}
/** 注釋 Ⅰ 的實現(xiàn)
* 在ActivityThread.java中
*/
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
String reason) {
//省略部分代碼..
r.activity.performResume(r.startsNotResumed, reason); //注釋 Ⅱ
}
/** 注釋 Ⅱ 的實現(xiàn)
*在 Activity.java 中
*/
final void performResume(boolean followedByPause, String reason) {
//省略其他代碼...
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this); //注釋 Ⅲ
}
/** 注釋 Ⅲ 的實現(xiàn)
* Instrumentation.java中
*/
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
//在這里就真正執(zhí)行了Activity的onResume()方法的調(diào)用
activity.onResume();
//省略部分代碼..
}
因此在ActivityThread類中的handleResumeActivity()里面調(diào)用performResumeActivity()完成了調(diào)用Activity類的onResume()方法的調(diào)用万哪。緊接在handleResumeActivity()方法執(zhí)行完performResumeActivity()后侠驯,又執(zhí)行了wm.addView() (注釋 Ⅳ),完成View的添加(包括測量奕巍、布局吟策、繪制)。
在handleResumeActivity()方法中的wm.addView()添加view伍绳,即調(diào)用了WindowManager的實現(xiàn)了WindowManagerImpl中的addView()方法踊挠,然后在乍桂。WindowManagerImpl中的addView()方法里面又調(diào)用WindowManagerGlobal的addView()方法去添加view
/**
* WindowManagerImpl.java
*/
public final class WindowManagerImpl implements WindowManager {
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); //注釋 Ⅴ
}
}
/** 注釋 Ⅴ 的實現(xiàn)
* WindowManagerGlobal.java
*/
public final class WindowManagerGlobal {
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);
//添加view到List集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//root是ViewRootImpl 在里面實現(xiàn)view的添加
root.setView(view, wparams, panelParentView); //注釋 Ⅵ
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
}
在ViewRootImpl中的setView()方法里面會調(diào)用requestLayout()方法請求布局
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
/** 注釋 Ⅵ 的實現(xiàn)
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//省略部分代碼....
// 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(); //注釋 Ⅶ
//省略部分代碼....
}
/** 注釋 Ⅶ 的實現(xiàn)
* 在requestLayout()中調(diào)用scheduleTraversals()
*/
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals(); //注釋Ⅷ
}
}
/** 注釋 Ⅷ 的實現(xiàn)
*在scheduleTraversals()方法中有個異步操作mTraversalRunnable
*/
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //注釋 Ⅸ
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
/** 注釋 Ⅸ 的實現(xiàn)
*ViewRootImpl里的內(nèi)部類冲杀,執(zhí)行doTraversal()
*/
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal(); // 注釋 Ⅹ
}
}
/** 注釋 Ⅹ 的實現(xiàn)
*在doTraversal方法中執(zhí)行 performTraversals();
*/
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//在此方法中真正調(diào)用了ViewGroup的布局、測量睹酌、繪制
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
/**
* 在performTraversals()方法中執(zhí)行View的測量权谁、布局、繪制
*/
private void performTraversals() {
//省略部分代碼..
// Ask host how big it wants to be
//測量憋沿、布局旺芽、繪制
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
}
在ViewRootImpl類中 performMeasure()的實現(xiàn)
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//開始view的測量,這里的 mView是 DecorView
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
在ViewRootImpl類中 performLayout()的實現(xiàn)
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
//開始布局 這里的host是 DecorView
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
//省略部分代碼....
}
在ViewRootImpl類中performDraw()的實現(xiàn)
private void performDraw() {
//省略其他代碼....
//開始繪制
boolean canUseAsync = draw(fullRedrawNeeded);
}
之所以在onCreate()辐啄、onStart()采章、onResume()里通過View.getHeight 和 View.getMeasuredHeight()方法拿到的都是0,是因為在onResume()方法執(zhí)行完后才進行view的添加(包括測量壶辜、布局悯舟、繪制)。