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;
}
可以看出action要么是直接取出 attachInfo 里的 handler 放到队列里执行。 要么是放到 HandlerActionQueue 中执行。
所以,接下来,我们就开始分析:
attachInfo什么时候初始化,- 初始化
attachInfo后,为什么能够获取View的宽高。 HandlerActionQueue什么时候处理消息。
attachInfo 什么时候初始化
通过以下方式:
问题转移到了 dispatchAttachedToWindow() 什么时候被调用。 通过「alt+F7」
发现是 ViewGroup 在执行 dispatchAttachedToWindow() 会:
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info, combineVisibility(visibility, child.getVisibility()));
}
源头找到了, 那就是 ViewGroup 的 dispatchAttachedToWindow() 完成了每个 view 的 attachInfo 初始化。
那 ViewGroup 的 dispatchAttachedToWindow() 又是谁调用呢? 这需要有一点经验,具体来说,大家都知道我们的布局文件在经过 xmlParser 解析后,会被添加到 DecorView. 所以我有理由相信,DecorView 就是第一个调用 dispatchAttachedToWindow() 的 ViewGroup
问题又发生了转移,DecorView 什么时候调用 dispatchAttachedToWindow() 呢? 此时是不是陷入的僵局,确实如此,这个我找了很久,都没找到,直到我在写最近这篇这次彻底搞明白子线程到底能不能更新 UI时,发现 ViewRootImp的构造方法里完成了 attachInfo的初始化, 然后在执行 performTraversals()时,会 host.dispatchAttachedToWindow(mAttachInfo, 0); 这里的host就是 DecorView.
结论:我们平时写在布局文件里的view其attachInfo属性是在performTraversals()里完成的
初始化attachInfo后,为什么能够获取 View 的宽高。
大家可能注意到了, 在在 attachInfo 被下发至 view 时, 并没有开始执行 view 的三大流程。 可为什么我们能通过view.post获取呢? 这就需要理解 Android的消息驱动机制,即Android方法的执行其实是handler处理消息的结果。所以,在执行 performTraversals() 时,其实是handler还在处理view 的三大流程对应的消息,只有这个消息处理后, 才会执行通过attachInfo发送的消息,此时当然能够获取宽高啦。
再进一步,attachInfo.mHandler.post(action)里的 handler又是哪里来的呢?毕竟是这个mHandler帮我们完成 action的发送和处理。 在 ViewRootImp中可以发现 final ViewRootHandler mHandler = new ViewRootHandler();所以 mHandler就是主线程里的 handler。
attachInfo.mHandler.post(action) 的意义就是:将action发送到主线程的消息队列中,并在绘制流程完成后开始执行action
HandlerActionQueue什么时候处理消息
走进HandlerActionQueue寻找其实例方法,显然:
通过 「alt+F7」,
这两个方法为什么能拿到 View 的宽高,前面已经分析过,不做赘述。
总结
基于Android的消息机制,View.post(action)里的action可以在view 走完 onMeasure()、onDraw()、onLayout()后执行,从而能够正确的获取View的宽高。