參考:
https://blog.csdn.net/Kitty_Landon/article/details/79235418
https://blog.csdn.net/scnuxisan225/article/details/49815269
其實這兩個的主要區(qū)別是看目前界面有沒有顯示出來或者說在調(diào)用這兩個方法時,view的dispatchAttachedToWindow方法有沒有執(zhí)行,如果已經(jīng)執(zhí)行過了,那么他們是沒啥區(qū)別的,都是利用Handler來發(fā)送Message到MessageQueue,如果dispatchAttachedToWindow還沒有執(zhí)行,那么他們是有區(qū)別的,最直觀的判斷就是我們可以在onCreate方法中利用view.post(runnable)來獲取view的寬高,但是無法利用handler.post(runnable)來獲取。
為什么在onCreate方法中可以利用view.post(runnable)獲取view的寬高?
看下源代碼:
View#post()
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;
}
我們假設(shè)目前attachInfo為null(其實目前它就是為null的,哪兒賦值的,后面分析)
/**
* Returns the queue of runnable for this view.
*
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
HandlerActionQueue#post()
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
可以看到,在調(diào)用了view的post方法之后只是將runnable存放到了HandlerAction的數(shù)組中,并沒有去執(zhí)行,那么什么時候執(zhí)行呢?
存放在HandlerAction數(shù)組中的runnable什么時候執(zhí)行?
HandlerActionQueue#executeActions()
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;
}
}
在這個方法里面會調(diào)用handler來post之前存放的Runnable,看下這個方法是什么時候執(zhí)行的?

dispatchAttachedToWindow是什么時候執(zhí)行的呢?

我們知道ViewRootImpl的performTraversals會執(zhí)行view的measure、layout、draw,大致是這樣:
host.dispatchAttachedToWindow()
...
performMeasure();
...
performLayout();
...
performDraw();
理一下:系統(tǒng)調(diào)用ViewRootImpl#performTraversals()方法,performTraversals()方法調(diào)用host的dispatchAttachedToWindow()方法,host就是DecorView也就是View,接著在View的dispatchAttachedToWindow()方法中調(diào)用mRunQueue.executeActions()方法,這個方法內(nèi)部會遍歷HandlerAction數(shù)組,利用Handler來post之前存放的Runnable。
問題來了,dispatchAttachedToWindow方法是在performMeasure方法之前調(diào)用的,既然在調(diào)用的時候還沒有執(zhí)行performMeasure來進行測量,那么為什么在執(zhí)行完dispatchAttachedToWindow方法后就可以獲取到寬高呢?
因為Android系統(tǒng)的運行完全是基于消息驅(qū)動的。
在調(diào)用完dispatchAttachedToWindow方法之后,會將之前調(diào)用view.post(runnable)中的runnable取出來執(zhí)行,這里的執(zhí)行其實是發(fā)送Message到MessageQueue,等待Looper來調(diào)用執(zhí)行,但是也得系統(tǒng)處理完上一個Message
而ViewRootImpl的performTraversals所處的環(huán)境正是一個Runnable對象,這個Runnable也是包裝成Message交給Handler來處理的,所以View.post(runnable)中的runnable執(zhí)行是要在performTraversals方法之后的,并非一調(diào)用dispatchAttachedToWindow就會執(zhí)行。
這個流程分析完了,我們回到View的post方法中
attachInfo什么時候被賦值的呢?
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;
}

ViewRootImpl是什么時候被初始化的呢?
在Activity的onResume()方法之后。

