疑问

1 Android 走生命周期的时候,View究竟是在哪个生命周期进行测量布局绘制的呢?
2 在该生命周期中通知View的绘制流程是怎样的?

解答

1 Android的resume周期开始绘制的,这也是我们为什么在onResume的时候获取不到view的狂高
2 下面来看下具体流程(view的绘制是通过ViewRootImp的scheduleTraversals方法,因此看下怎么从ActivityThread找到ViewRootImp的scheduleTraversals的调用的)

直接来看ActivityThread.java 中的handleResumeActivity中的处理,代码如下

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;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//wm被addView
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}

上面的wm是ViewManager的getWindow(),而Activity的getWindowManager()最终返回的对象是WindowManager

public WindowManager getWindowManager() {
return mWindowManager;
}

再来看Activity的mWindowManager 是何时创建的呢?请看下面attach方法中的代码,setWindowManager

.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

这时点击到setWindowManager方法(如下)查看发现,​​mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);​​,至此也就是addView方法最终的调用者,实际是WindowManagerImpl对象,因此我们需要去查看WindowManagerImpl的addView方法做了什么操作

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

现在来看WindowManagerImpl的addView方法做了什么

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

WindowManagerImpl最终通过mGlobal对象进行的addView,而mGlobal是一个WindowManagerGlobal对象,WindowManagerGlobal中的addView方法中有如下代码,

= 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 {
//ViewRootImpl的setView方法
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}

ViewRootImpl的setView方法中调用了​​requestLayout();​​​,与​​view.assignParent(this);​​,关于assignParent()需要关联其他代码,为了避免思路有分歧,这里不介绍它。继续看下面的requestLayout

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

这里requestLayout 调用到scheduleTraversals()方法,scheduleTraversals方法中通过postSyncBarrier来插入一个消息屏障,其中又通过Choreographer对象发送mTraversalRunnable,

@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

来看看看TraversalRunnable被调用的时候做了什么?调用了doTraversal()

final class TraversalRunnable implements Runnable {
@Override
public void 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;
}
}
}

之后在中可以看到调用了​​performLayout(lp, mWidth, mHeight);​​从而进一步调用如下代码:

measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

通过上面的measureHierarchy和host.layout来进行后续的测量与布局。

小结

ActivityThread—》handleResumeActivity—》addView–》WindowManagerImp—》WindowManagerGlobal–》addView—》ViewRootImpl—》setView—》requestLayout()–》scheduleTraversals()–》doTraversal()----》performTraversals()—》测量与布局

流程经过的类:ActivityThread、WindowManagerImp、WindowManagerGlobal、ViewRootImpl