Skip to content

2019-08-21:View.post()为什么可以获取到宽高信息? #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Moosphan opened this issue Aug 21, 2019 · 13 comments
Open

2019-08-21:View.post()为什么可以获取到宽高信息? #126

Moosphan opened this issue Aug 21, 2019 · 13 comments

Comments

@Moosphan
Copy link
Owner

我们可能会在项目中经常这么做:

 view.post(new Runnable() {
            @Override
            public void run() {
                 Logger.e("当前 view 的高度为:${view.getHeight()}");
            }
        });

这么做是可以获取到 view 的宽高信息的,但是大家有想过为什么吗?

@FeanCheng
Copy link

不清楚哈哈哈哈 不过貌似 这样子做 会把任务推到最后 直到当前view绘制完成后 才能正常获取到宽高 当然 还有其他三种方法 可以正确获取到

@DaveBoy
Copy link

DaveBoy commented Aug 21, 2019

因为view没绘制的时候post是添加到view内部的一个队列中,view创建好之后(啥viewattachwindows方法分发的时候会一起调用,而这个方法在view绘制完成之后执行,所以能拿到)

@gabyallen
Copy link

其实我们调用的post方法执行流程有两种,一种是直接使用Handler去执行,第二是将我们的runabale存储到队列中。

@MrShuHong
Copy link

这个问题讲清楚有点难度呀。 内部执行是通过handler 去执行的, View.post 如果在onCreate方法中调用,这时会把Runnable保存到一个缓存数组中,等到View的 dispatchAttachedToWindow 方法被调用时,去通过handler执行Runnable方法, dispatchAttachedToWindow就是View加载到Window时被调用的。https://www.cnblogs.com/dasusu/p/8047172.html

@bukeCN
Copy link

bukeCN commented Aug 22, 2019

这样写一般是在 Activity 的 onResume 方法中,因为 onResume 执行在 View 初始化之前,如果在 onResume 中直接获取 View 宽高是获取不到的。使用 view.post 就能获取到,因为 view.post 是向 主 Handler 的 MessageQueu 中插入一条带执行消息,但是因为系统在 ViewRoot 中初始化 View 时也是利用 Handler 机制,平且为了优先执行 View 的初始化设置了同步屏障,导致 view.post 插入的消息会在 View 初始化之后执行,那么肯定就能获取到 View 的宽高啦!

@Fritz-Xu
Copy link

Fritz-Xu commented Aug 22, 2019

View.post方法调用时,如果在 View 还没开始绘制时( Activity 的 onResume方法还没回调之前 或者onResume方法执行了,但是 ViewRootImpl 的 performTraversals 还没开始执行)就会用一个初始长度为 4 的数组缓存起来(Runnable 数量大于4时会进行扩容),ViewRootImpl 在初始化时创建了一个 View.AttchInfo 对象并绑定 ViewRootImpl 的 Handler ,该 Handler 也用于发送 View 绘制相关的 msg ;

等到 ViewRootImpl执行 performTraversals方法时(此时 Activity 已经回调了onResume),会配置 View 的 AttchInfo 对象并且通过 View 的 dispatchAttachedToWindow 方法传入到 View 里面完成绑定,在该方法中会取出 View 缓存的 Runnable 并用 View.AttchInfo 的 Handler 来进行 post 方法,这样子就会加入到 MessageQueue 里面进行排队,等到这些缓存 Runnable 执行时,主线程里面 View的绘制流程也就结束了,所以这时候 Looper 取出这些缓存的Runnable 执行时就可以拿到 View 的宽高

那么,什么情况下View.post 方法不会执行呢?如果 Activity 因为某些原因没有执行到 onResume 的话,无法顺利调用 ViewRootImpl 的 performTraversals 的话,View.post 方法就不会执行
========================(更新分割线)==============================
刚朋友提醒,如果 View 是 new 出来的,并且没有通过 addView 等方法依赖到 DecorView 上面,它的 post 方法也是不会执行的,因为它没有机会和 ViewRootImpl 进行互动了

@chenqi5256969
Copy link

一个view的大小的最终确定是在onlayout中,但是当我们打开Activity,执行onCreate方法的时候,view可能还没执行到onlayout方法,这也是为什么我们在onCreate的方法中,通过getWidth获取的宽度为0的原因,但是在View.post方法中,实现了一个Runnable,即添加了一个队列任务,在执行完setContentView方法之后,队列任务里面会多一条询问布局是否完成的任务,我们添加的这一个任务就是加在这个任务之后,所以通过view.post这个方法可以获取到view的宽和高

@Alex-Cin
Copy link

大概几点, 1.. Window 2.. viewGroup.addView 或者 activity.setContentView 引起 View 树变化;
就会调用 requestLayout, 当收到 vSync 信号之后, 会在 ViewRootImpl 调用 performTraversals , 进而会 measure 所有的view节点, 之后, 会回调 myView.post{ 放进去的 runnable};
往 decorView 或者 window 上动态添加 view的时候, 有时候需要算 各个 view 的宽高, 用 myView.post 这种形式最好的, 免去 手动measure消耗的时间, 而且这个数值, 一定是最终呈现的, 不会错;

@aositeluoke
Copy link

1、LAUNCH_ACTIVITY,SystemServer进程发起,在这个消息里边将view.post中提交的runnable对象缓存起来,当执行到dispatchAttachedToWindow方法时,将缓存中的runnable插入消息队列,然后执行performMeasure、performLayout、performDraw方法
2、此处就是post的Runnable消息了,当上面的消息处理结束后,就轮到我了,由于上述已经对视图已经测量过了,所以这里能拿到宽高

@zxw1962
Copy link

zxw1962 commented Jan 6, 2023

最根本原因还是View#post这种方式提交的runnable的执行是在view树绘制完成之后才真正执行的。

@luckilyyg
Copy link

luckilyyg commented Jan 6, 2023 via email

@Empty0Qc
Copy link

Empty0Qc commented Jan 6, 2023 via email

@flyisme
Copy link

flyisme commented Jun 21, 2024

个人理解:

  1. mAttachInfo 是ViewRootImpl performTraversals 时绑定给DectorView的 此前会开启消息屏障(只接受异步消息),绘制结束后移除屏障!
  2. attachInfo.mHandler.post(action); 发送的是普通消息。只有消息屏障被移除后才可以执行(移除时机:View渲染完成,此时已完成了绘制)。
  3. 如上:借助消息屏障确保 post 消息是在View渲染完成后执行

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests