View.post()不靠谱的地方你知道多少
首先我们来看一段代码:
![](https://img.haomeiwen.com/i4471798/75342759e80c4a80.png)
启动了两个模拟器 API 22 和API 26分别是安卓7.0以下和安卓7.0以上
![](https://img.haomeiwen.com/i4471798/43f890b9e276b60a.png)
下面我们就从源码分析他们之间的差别以及为啥造成的现象不同
首先我们来分析安卓7.0以下的源码流程:
![](https://img.haomeiwen.com/i4471798/9b0a92b9bf84473a.png)
![](https://img.haomeiwen.com/i4471798/0a405951d96b939e.png)
当attachInfo为空时走的是ViewRootImpl.getRunQueue().post(action);代码 当attachInfo不为空时,API26上下没区别。
ViewRootImpl可以理解是一个Activity的ViewTree的根节点的实例。每个ViewRootImpl就是用来管理DecorView和ViewTree。
ViewRootImpl的用来承载Runnable的队列是sRunQueues,它是一个静态变量,也就是说在APP的生命周期内,ViewRootImpl中的消息队列就是这一个。
我们再来看看前面提到的ViewRootImpl.getRunQueue.post()到底干了什么?
![](https://img.haomeiwen.com/i4471798/3b7458fd0426f61e.png)
接下来我们再看哪里消耗mActions里面的Runnable
![](https://img.haomeiwen.com/i4471798/f6011533a7d3d9aa.png)
继续看executeActions在哪调用的
![](https://img.haomeiwen.com/i4471798/0ba0bc11ef3de71a.png)
可以发现android.view.ViewRootImpl#performTraversals调用到的,而performTraversals()又是在android.view.ViewRootImpl#doTraversal调用的,它又是在
![](https://img.haomeiwen.com/i4471798/af1e427704d65eaa.png)
在TraversalRunnable中执行,
![](https://img.haomeiwen.com/i4471798/13625d22cc3193d5.png)
所以在API23以下,executeAction() 是会被循环调用,基本上其内的mActions只要有未执行的Runnable立刻就会被消耗掉。
所以在API23以下的设备上,View.post基本上是靠谱的,post出去的都是有机会执行的。
接下来分析API24的实现细节
![](https://img.haomeiwen.com/i4471798/ff98c7976d631927.png)
![](https://img.haomeiwen.com/i4471798/573fe0db42cc2435.png)
同样类似的和API24以下,关键看下executeActions在哪执行,直接按住ctrl加上鼠标左键单击
![](https://img.haomeiwen.com/i4471798/4ed229f9d563e094.png)
既然executeActions()方法,在API24以上,只有在dispatchAttachedToWindow() 方法中,才有机会被调用到,而View.dispatchAttachedToWindow()方法,只有在View通过 addView()等方法中加入到一个ViewGroup的时候,才会被调用到,这就导致直接写在Layout 布局中的控件没有被addView的话,那么它永远也不会执行。这就导致现象不一致的缘故。
注意:上述都是分析的是 mAttachInfo 已经为空的情况,不为空是没有区别的。
可以尽量避免使用View.post()方法,直接使用Handler.post方法来替代。