这次让你彻底的明白为什么能在View.post 中获取 view的宽高

1,677 阅读2分钟
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()));
}

源头找到了, 那就是 ViewGroupdispatchAttachedToWindow() 完成了每个 viewattachInfo 初始化。

ViewGroupdispatchAttachedToWindow() 又是谁调用呢? 这需要有一点经验,具体来说,大家都知道我们的布局文件在经过 xmlParser 解析后,会被添加到 DecorView. 所以我有理由相信,DecorView 就是第一个调用 dispatchAttachedToWindow()ViewGroup

问题又发生了转移,DecorView 什么时候调用 dispatchAttachedToWindow() 呢? 此时是不是陷入的僵局,确实如此,这个我找了很久,都没找到,直到我在写最近这篇这次彻底搞明白子线程到底能不能更新 UI时,发现 ViewRootImp的构造方法里完成了 attachInfo的初始化, 然后在执行 performTraversals()时,会 host.dispatchAttachedToWindow(mAttachInfo, 0); 这里的host就是 DecorView.

结论:我们平时写在布局文件里的viewattachInfo属性是在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的宽高。