//View
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
...
}
view调用post时,先会进行 mAttachInfo != null 的判断,而 mAttachInfo 是在view加载到Window中的时候才进行的赋值。
当view还未被加载到window时,会先将 action 放到 getRunQueue() 中, getRunQueue 就是mRunQueue
当view加载到window后,会从mRunQueue中取出之前的action。
//HandlerActionQueue
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
//ViewRootImpl
private void performTraversals() {
...
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
//不会立即执行,只会将任务先加到队列中,等 performTraversals 执行完毕后才轮到它
getRunQueue().executeActions(mAttachInfo.mHandler);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
这一块很奇怪,按顺序走,dispatchAttachedToWindow 其实是在 performMeasure 之前被调用的,这难到是有可能post不一定能获取到宽高吗?
进到 executeActions 看,发现里面的实现其实是将 action 再次传入到 mAttachInfo.mHandler 中,根据
ViewRootImpl 的初始化方法发现 , 此 handler 为:
//ViewRootImpl
public ViewRootImpl(Context context, Display display) {
...
mChoreographer = Choreographer.getInstance();
...
}
final ViewRootHandler mHandler = new ViewRootHandler();
void doTraversal() {
...
performTraversals();
...
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
...
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
//Choreographer
public static Choreographer getInstance() {
return sThreadInstance.get();
}
private Choreographer(Looper looper, int vsyncSource) {
...
mHandler = new FrameHandler(looper);
...
}
我们回到 performTraversals 的调用上,可以发现是由 scheduleTraversals -> mChoreographer -> mTraversalRunnable -> doTraversal 进行的调用。
进一步观察 mChoreographer ,发现其创建是跟随着 ViewRootImpl 的创建,其内部的 handler 与ViewRootImpl 是相同线程,相同的 loop
再解释一下:
performTraversals 方法 先执行 host.dispatchAttachedToWindow(mAttachInfo, 0);
然后执行 getRunQueue().executeActions(mAttachInfo.mHandler);
由于 performTraversals 本身就是在 handler 中运行 , executeActions 并不会立马执行,只是将 message 加到队列中。
之后就会去执行 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
等到 measure 执行完成后,下一个 message 才是获取宽高的代码
那么回到handler的消息机制,action 是在消息队列中循环调用,需要上一次执行完才能执行下一个,那么首先会执行完 TraversalRunnable 也就是说 Measure Layout 会先运行完,再去运行我们加到handler里的代码,从而就保证了view.post 能获取到宽高。