//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調(diào)用post時(shí),先會(huì)進(jìn)行 mAttachInfo != null 的判斷,而 mAttachInfo 是在view加載到Window中的時(shí)候才進(jìn)行的賦值。
當(dāng)view還未被加載到window時(shí),會(huì)先將 action 放到 getRunQueue() 中, getRunQueue 就是mRunQueue
當(dāng)view加載到window后,會(huì)從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);
...
//不會(huì)立即執(zhí)行,只會(huì)將任務(wù)先加到隊(duì)列中,等 performTraversals 執(zhí)行完畢后才輪到它
getRunQueue().executeActions(mAttachInfo.mHandler);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
這一塊很奇怪,按順序走,dispatchAttachedToWindow 其實(shí)是在 performMeasure 之前被調(diào)用的,這難到是有可能post不一定能獲取到寬高嗎?
進(jìn)到 executeActions 看,發(fā)現(xiàn)里面的實(shí)現(xiàn)其實(shí)是將 action 再次傳入到 mAttachInfo.mHandler 中,根據(jù)
ViewRootImpl 的初始化方法發(fā)現(xiàn) , 此 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 的調(diào)用上,可以發(fā)現(xiàn)是由 scheduleTraversals -> mChoreographer -> mTraversalRunnable -> doTraversal 進(jìn)行的調(diào)用。
進(jìn)一步觀察 mChoreographer ,發(fā)現(xiàn)其創(chuàng)建是跟隨著 ViewRootImpl 的創(chuàng)建,其內(nèi)部的 handler 與ViewRootImpl 是相同線程,相同的 loop
再解釋一下:
performTraversals 方法 先執(zhí)行 host.dispatchAttachedToWindow(mAttachInfo, 0);
然后執(zhí)行 getRunQueue().executeActions(mAttachInfo.mHandler);
由于 performTraversals 本身就是在 handler 中運(yùn)行 , executeActions 并不會(huì)立馬執(zhí)行,只是將 message 加到隊(duì)列中。
之后就會(huì)去執(zhí)行 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
等到 measure 執(zhí)行完成后,下一個(gè) message 才是獲取寬高的代碼
那么回到handler的消息機(jī)制,action 是在消息隊(duì)列中循環(huán)調(diào)用,需要上一次執(zhí)行完才能執(zhí)行下一個(gè),那么首先會(huì)執(zhí)行完 TraversalRunnable 也就是說 Measure Layout 會(huì)先運(yùn)行完,再去運(yùn)行我們加到handler里的代碼,從而就保證了view.post 能獲取到寬高。