View.post() 不靠谱的地方你知道多少


Posted in Javascript onAugust 29, 2017

一、前言

有时候,我们会需要用到 View.post() 方法,来将一个 Runnable 发送到主线程去执行。这一切,看似很美好,它最终会通过一个 Handler.post() 方法去执行,又避免我们重新定义一个 Handler 对象。

但是,从 Android 7.0(Api level 24) 开始,View.post() 将不再那么靠谱了,你 post() 出去的 Runnable ,可能永远也不会有机会执行到。

二、post 在 7.0 的差异

2.1 post 方法的差异

前面提到,这个问题只出现在 Android 7.0 上。那么就先从源码分析 Android 7.0 到底对 View.post() 做了什么改动。

View.post() 不靠谱的地方你知道多少

用 Diff 看一下它们的差异,左边是 Api Level 24+(以下简称 Api24) 的代码,右边是 Api level 23-(以下简称 Api23) 的代码。

很明显的可以看出来,它们只有在 mAttachInfo 为 null 的时候,执行的逻辑才会有差异。

Api24 中,会调用 getRunQueue().post(action),而 Api23 会调用 ViewRootImpl.getRunQueue().post(action) 方法,他们的差异就在这里。

2.2 Api23 post 的细节

先简单理解一下,ViewRootImpl 是什么。

ViewRootImpl 可以理解是一个 Activity 的 ViewTree 的根节点的实例。每个 ViewRootImpl 就是用来管理 DecorView 和 ViewTree。

ViewRootImpl 中的用来承载 Runnable 的队列是 sRunQueues ,它一个静态的变量,也就是说在 App 的生命周期内,ViewRootImpl 中的这个消息队列都是同一个。

再来看看前面提到的 ViewRootImpl.getRunQueue().post() 到底干了什么?

View.post() 不靠谱的地方你知道多少

post() 方法只是单纯的将它包装成一个 HandlerAction 对象,然后放入 mActions 这个 ArrayList 中。继续追查下去就需要知道 mActions 中添加的 HandlerAction 在何时被消费掉了。

消费 HandlerAction 的地方,是 executeActions() 方法。

View.post() 不靠谱的地方你知道多少

它最终,还是调用的 handler.postDelayed() ,这没什么好说的,关键点在于 executeAction() 方法,是在什么时候被调用的。

executeAction() 是被 TraversalRunnable 调用 doTraversa() ,在doTraversa() 方法中,进行调用的。而 TraversalRunnable 又是通过 Choreographer.postCallBack() 去循环调用的。这个 Choreographer 通过 doScheduleCallback() 发送一个 MSG_DO_SCHEDULE_CALLBACK 类型的消息循环调用,间隔就是一个 VSync 的间隔。

关于 Choreographer ,不是本文的重点,有兴趣可以单独了解一下。

所以,在 Api23 以下,executeAction() 是会被循环调用,基本上其内的 mActions 只要有未执行的 Runnable 立刻就会被消费掉。

所以在 Api23 以下的设备上,View.post() 基本上是靠谱的,post 出去的 Runnable 都会有机会执行到。

2.3 Api24 的细节

再来看看在 Api24 中的实现细节,在 Api24 中,调用的是 getRunQueue().post() 方法,它操作的是一个 HandlerActionQueue 对象。

View.post() 不靠谱的地方你知道多少

内部的结构其实和 Api23 很像,也是维护了一个 HandlerAction 的数组 mActions 。

最终消费掉 mActions 的地方,依然是一个 executeActions() 方法。

View.post() 不靠谱的地方你知道多少

回到根本的问题,executeActions() 方法在什么时机会被调用到,继续追查可以看到它在 View.dispatchAttachedToWindow() 方法中,会被调用。

View.post() 不靠谱的地方你知道多少

既然,executeActions() 方法,在 Api24 及以上,只会在 dispatchAttachedToWindow() 的方法中,才有机会被调用到,而 View.dispatchAttachedToWindow() 方法,只有在这个 View 通过 addView() 等方法,加入到一个 ViewGroup 的时候,才会被调用到。这就导致写在 Layout 布局中的控件,是不会有机会再调用 addView() 方法的,所以它永远也得不到执行。这也就到时了 Api24 下,View.post() 表现的现象不一致的缘故。

三、小结

View.post() 方法,在不同版本的差异,根本原因还是在于 Api23 和 Api24 中,executeActions() 方法的调用时机不同,导致 View 在没有 mAttachInfo 对象的时候,表现不一样了。

所以我们在使用的过程中需要慎用,区分出实际使用的场景,一般规范自己的代码即可:

在 View 已经被显示出来之后,再调用 View.post() 方法(这个时候 mAttachInfo 已经不为空了)。

尽量避免使用 View.post() 方法,可以直接使用 Handler.post() 方法来替代。

总结

以上所述是小编给大家介绍的View.post() 不靠谱的地方,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js实现的网页颜色代码表全集
Jul 17 Javascript
jquery操作下拉列表、文本框、复选框、单选框集合(收藏)
Jan 08 Javascript
javascript初学者常用技巧
Sep 02 Javascript
Node.js node-schedule定时任务隔多少分钟执行一次的方法
Feb 10 Javascript
javascript实现漂亮的拖动层,窗口拖拽特效
Apr 24 Javascript
jquery控制显示服务器生成的图片流
Aug 04 Javascript
js点击列表文字对应该行显示背景颜色的实现代码
Aug 05 Javascript
JavaScript判断FileUpload控件上传文件类型
Sep 28 Javascript
javascript html5 canvas实现可拖动省份的中国地图
Mar 11 Javascript
jquery实现简单Tab切换菜单效果
Jul 17 Javascript
JS实现浏览上传文件的代码
Aug 23 Javascript
vuex实现购物车功能
Jun 28 Javascript
jsTree事件和交互以及插件plugins详解
Aug 29 #Javascript
移动端Ionic App 资讯上下循环滚动的实现代码(跑马灯效果)
Aug 29 #Javascript
基于jstree使用AJAX请求获取数据形成树
Aug 29 #Javascript
基于jQuery的左滑出现删除按钮的示例
Aug 29 #jQuery
jQuery实现简单的计时器功能实例分析
Aug 29 #jQuery
Vue2.0权限树组件实现代码
Aug 29 #Javascript
基于Vue实现后台系统权限控制的示例代码
Aug 29 #Javascript
You might like
PHP 文件上传功能实现代码
2009/06/24 PHP
PHP中用接口、抽象类、普通基类实现“面向接口编程”与“耦合方法”简述
2011/03/23 PHP
jquery text()要注意啦
2009/10/30 Javascript
javascript淡入淡出效果的实现思路
2012/03/31 Javascript
获取div编辑框,textarea,input text的光标位置 兼容IE,FF和Chrome的方法介绍
2012/11/08 Javascript
javascript中parentNode,childNodes,children的应用详解
2013/12/17 Javascript
jquery的ajax简单结构示例代码
2014/02/17 Javascript
js实现键盘操作实现div的移动或改变的原理及代码
2014/06/23 Javascript
javascript实现的右下角弹窗实例
2015/04/24 Javascript
js插件Jcrop自定义截取图片功能
2016/10/14 Javascript
jQuery模拟实现的select点击选择效果【附demo源码下载】
2016/11/09 Javascript
Angular实现搜索框及价格上下限功能
2018/01/19 Javascript
Vue源码中要const _toStr = Object.prototype.toString的原因分析
2018/12/09 Javascript
Vue+penlayers实现多边形绘制及展示
2020/12/24 Vue.js
[47:42]完美世界DOTA2联赛PWL S2 GXR vs Ink 第一场 11.19
2020/11/20 DOTA
python django 访问静态文件出现404或500错误
2017/01/20 Python
python 应用之Pycharm 新建模板默认添加编码格式-作者-时间等信息【推荐】
2019/06/17 Python
使用matlab或python将txt文件转为excel表格
2019/11/01 Python
python-xpath获取html文档的部分内容
2020/03/06 Python
详解python中GPU版本的opencv常用方法介绍
2020/07/24 Python
解析python 类方法、对象方法、静态方法
2020/08/15 Python
ubuntu16.04升级Python3.5到Python3.7的方法步骤
2020/08/20 Python
python反爬虫方法的优缺点分析
2020/11/25 Python
推荐10个CSS3 制作的创意下拉菜单效果
2014/02/11 HTML / CSS
html5指南-4.使用Geolocation实现定位功能
2013/01/07 HTML / CSS
英国女性时尚品牌:Apricot
2018/12/04 全球购物
链表面试题-一个链表的结点结构
2015/05/04 面试题
单位办理社保介绍信
2014/01/10 职场文书
党的群众路线教育实践活动心得体会(企业)
2014/11/03 职场文书
应届毕业生求职简历自我评价
2015/03/02 职场文书
党员自我评价2015
2015/03/03 职场文书
大学校园招聘会感想
2015/08/10 职场文书
python自然语言处理之字典树知识总结
2021/04/25 Python
Django实现drf搜索过滤和排序过滤
2021/06/21 Python
探讨Java中的深浅拷贝问题
2021/06/26 Java/Android
mysql拆分字符串作为查询条件的示例代码
2022/07/07 MySQL