讲解前,首先看一个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方法
这里判断了atttachInfo是否为null,假设我们在activity的onCreate中调用view.post方法,这时候attachInfo为null,为什么是null,这里先留一个疑问,我们后面在分析
接着看getRunQueue().post
可以看出,这里只是把action存在HandlerAction这个数组中了,接着我们需要知道这个数组什么时候会执行
这里可以看出,我们设置的runnable请求,最后通过executeActions传入的handler处理了
通过as的搜索,我们知道mRunQueue执行的时机是在view的dispatchAttachedToWindow中,那么dispatchAttachedToWindow在哪里被调用的呢
我们应该知道view的三大流程是在ViewRootImpl中实现的,那这个dispatchAttachedToWindow会不会在这个这里面被调用呢,我们在AS中双击Shit,搜索ViewRootImpl源代码,然后ctrl+F搜索
发现,在ViewRootImpl的performTraversals中调用了host的dispatchAttachedToWindow,这个host其实就是DecorView
,我们知道DecorView是一个ViewGroup,那么看一下ViewGroup的dispatchAttachedToWindow
这里调用了父类的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中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中调用
而doTraversal是在TraversalRunnable中被调用
这个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才会执行
到这里,我们就很清楚啦