view.post是如何保证获取到view的真实宽高的

讲解前,首先看一个handler使用的例子

		Handler handler1 = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				Log.d("handler", "handler 1 execute");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};

		Handler handler2 = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				Log.d("handler", "handler 2 execute");
			}
		};

		handler1.sendEmptyMessage(0);
		handler2.sendEmptyMessage(0);

这个"handler 1 excute"和"handler 2 excute"会立刻打印吗?

咱们看一下打印结果:

我们发现2比1晚了2s才打印,为什么呢?

原因很简单,两个handler都用了同一个Looper,消息是阻塞的,如果不明白handler的消息机制,可以看我之前的文章

Handler的底层实现

好了,有了这个基础我们接着分析view.post原理

 

看一下view.post方法

view.post是如何保证获取到view的真实宽高的_第1张图片

这里判断了atttachInfo是否为null,假设我们在activity的onCreate中调用view.post方法,这时候attachInfo为null,为什么是null,这里先留一个疑问,我们后面在分析

接着看getRunQueue().post

view.post是如何保证获取到view的真实宽高的_第2张图片

可以看出,这里只是把action存在HandlerAction这个数组中了,接着我们需要知道这个数组什么时候会执行

view.post是如何保证获取到view的真实宽高的_第3张图片

view.post是如何保证获取到view的真实宽高的_第4张图片

这里可以看出,我们设置的runnable请求,最后通过executeActions传入的handler处理了

通过as的搜索,我们知道mRunQueue执行的时机是在view的dispatchAttachedToWindow中,那么dispatchAttachedToWindow在哪里被调用的呢

我们应该知道view的三大流程是在ViewRootImpl中实现的,那这个dispatchAttachedToWindow会不会在这个这里面被调用呢,我们在AS中双击Shit,搜索ViewRootImpl源代码,然后ctrl+F搜索

view.post是如何保证获取到view的真实宽高的_第5张图片

view.post是如何保证获取到view的真实宽高的_第6张图片

 

发现,在ViewRootImpl的performTraversals中调用了host的dispatchAttachedToWindow,这个host其实就是DecorView

,我们知道DecorView是一个ViewGroup,那么看一下ViewGroup的dispatchAttachedToWindow

view.post是如何保证获取到view的真实宽高的_第7张图片

这里调用了父类的dispatchAttachedToWindow,也就是view的dispatchAttachedToWindow

到这里我们就明白了,view的dispatchAttachedToWindow原来是在ViewRootImpl的performTraversals中调用

整理一下目前能得到的结论:

1.view.post是通过handler,处理runnable请求的

2.runnable调用时机是在ViewRootImpl的performTraversals中

问题:executeActions中的handler是谁的?

要搞懂这个handler归属问题,我们需要知道AttachInfo在哪里创建,回到ViewRootImpl,我们在dispatchAttachedToWindow中传入了mAttachInfo,搜索这个mAttachInfo,

找到了这个handler

view.post是如何保证获取到view的真实宽高的_第8张图片

再看看我们前面提出的疑问,view.post中mAttachInfo的判空问题,view中mAttachInfo中赋值的唯一地方,是在view的dispatchAttachedToWindow,dispatchAttachedToWindow是在ViewRootImpl中performTraversals中调用,performTraversals调用时机比Activity的onCreate晚,所以在Activity的onCreate中调用view.post,mAttachInfo为null

ViewRootImpl是在主线程创建的,这里的mHandler肯定是在主线程

最后分析performTraversals,对安卓比较了解的同学都知道,这里面执行了view的三大流程,measure,layout,draw

performTraversals是在doTraversal中调用

view.post是如何保证获取到view的真实宽高的_第9张图片

而doTraversal是在TraversalRunnable中被调用

view.post是如何保证获取到view的真实宽高的_第10张图片

这个TraversalRunnable最后也是被主线程的Handler处理啦,下面在看performTraversals的部分实现

private void performTraversals() {
    ...
    
    host.dispatchAttachedToWindow(mAttachInfo, 0);
    ...
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...

}

我们发现,dispatchAttachedToWindow在performMeasure之前,我们知道performMeasure执行完毕,view的宽高也就基本确定了,那按理说我先去获取宽高,然后在去measure,获取的宽高结果应该为0,但事实上,我们是可以获得宽高的,问什么呢?

解释这个问题,需要回到我们的第一个实例,也就是handler问题,当在同一个Looper下,都在主线程,消息是阻塞的,也就是这时候handler会先执行完performTraversals中view三大流程,然后才会去执行dispatchAttachedToWindow,view中设置的runnable才会执行

到这里,我们就很清楚啦

 

你可能感兴趣的:(android,View)