Android中View.post和Handler.post的关系


Posted in Java/Android onJune 05, 2022

前言

View.post和Handler.post是Android开发中经常使用到的两个”post“方法,我们经常通过前者去获取一些View在运行时的渲染数据,或者测量页面的渲染时间。而后者则是Android的核心Handler的一个方法,它会向对应线程的MessageQueue中插入一条Message,在未来的某个事件点得到执行.....

为什么要拿这二者来比较?

首先,这二者的名字相同

其次,是View.post()的调用时机和整个View的绘制和渲染有着千丝万缕的联系。而这一切的基础,正是主线程的Handler.post(),理清这二者的关系,能够加深我们对View渲染、绘制的流程的理解。

View的渲染起点

宏观上来说,当DecorView被”attach“到Window之上后,程序能够收到系统分配给各个Activity的同步信号时,View就会开始渲染了,当每个同步信号到来时,ChoreoGrapher将会派发出一个信号通知ViewRootImpl进行视图的渲染,因此,从系统上来看,每次释放的Vsync同步信号应该是视图绘制的起点。

从App端来说,当ScheduleTravesals被调用时,会先向MessageQueue中插入一个消息屏障,此时会阻隔其他的同步消息的通过,允许异步消息的进入。然后mChoreoGrapher,向MessageQueue中插入一个视图更新的信号,最终会走到doTraversals()方法中,在该方法的执行过程中,将会先取消掉同步屏障,然后紧接着执行performTraversals()方法。显然,消息屏障的作用就是提升peformTraversals的优先级,确保视图的优先绘制。

不难发现,真正的进行渲染的起点是perfromTraversals()方法:

Android中View.post和Handler.post的关系

View.post的执行流程

View.post在不同版本的Android系统中,有着不同的实现,在API24以前,View.post所做的是:当View.post被调用时,直接向ViewRootImpl的mRunQueue中插入一个Runnable,然后在performTraversals()过程中,统一进行处理,这样一来,View.post()就会按照View.post()的调用顺序在”未来的某个时间点“进行执行,这说明:在这一系列的Android版本中,View.post的执行顺序就是本身调用View.post()的顺序

处理:这里的处理并非直接执行Runnable,而是统一插入到主线程的MessageQueue中去执行;

“未来的某个时间点”,这个未来的某个时间点指的是perfromTraversals()中将ViewRootImpl中mRunQueue中的所有Runnable插入到MessageQueue之后的某个时间点。必然在performTraversals()之后。

Android中View.post和Handler.post的关系

如上图,必须得等到整个perfromTraversals方法体执行完成(包括)后,才有可能执行下一个Message(这里标注为了Runnable),而perfromTraversals()方法体中,会顺序地调用performMeasure()、performLayout()、performDraw()方法,这三个方法走完,意味着视图已经完成了渲染,此时的View.post()执行,必然是能落在视图创建之后

而API24及之后的版本中,View.post所做的事情发生了改变,当View.post()调用时,Runnable被插入到View各自的mRunQueue当中,也就是说,每个View都含有一个mRunQueue,当performTraversals()中,也没有统一处理了,而是根据 performTraversals()->dispatchAttachedToWindows()递归地调用到子View时,子View将自己的mRunQueue插入到主线程的MessageQueue,这意味着:在高版本的执行过程中,View.post()的执行顺序是按照视图被迭代到的顺序。

不变的是View.post()执行,必然是能落在视图创建之后,这也是为什么能够调用View.post()来获取一些屏幕上的View的数据的原因。

Handler.post()能像View.post()一样获取到宽、高数据吗?

Activity为我们暴露了三个常用的生命周期函数:onCreate()、onStart()、onResume()。通常我们对一些事件的监听、View的初始化设置都会在这三个生命周期函数中实现,以最后执行的onReumse()为例,我们在其中使用主线程的Handler.post()获取一个视图的数据,

我们可以看看结果:

override fun onResume(){
        super.onResume()
        Handler(Looper.getMainLooper()).post{
            Log.d("getHeight",textView.height.toString())
        }
    }
D/getHeight: 0

显然,失败了。

我们知道,一个的Activity的创建初期,DecorView并不会直接就和Activity建立联系,建立联系的过程在handleResumeActivity()当中,此时的DecorView被attach到了Activity之上。但是,我们需要明确一点:一个View如果没有和Activity建立联系,那么它将收不到系统的同步信号,也就无法更新(更新也没有意义,因为它没有地方去显示),我们看看handleResumeActiivty的执行方法体,可以发现,先走了onResume()的回调,再走了a.mDecor = decor这一步骤,上文我们提到,视图更新的事件是以Message的形式,在MessageQueue中”排队“的,如果我们在onResume()中插入一个消息去获取渲染之后的宽高数据,那么这时的MessageQueue大概是这样:

Android中View.post和Handler.post的关系

当前正在执行的是黄色的Message,这是一个从ActivityThread.java中H类发出的调度方法,它将会调用到handleResumeActivity中的一系列方法,最终走到onResume这,我们使用Handler.post(),我们会发现消息被插在了黄色的Message之后,但是此时的a.mDecor = decor还没有执行,更不可能已经发生绘制了,这也就意味着压根没渲染,没视图,自然也没数据,完整的流程如下:

Android中View.post和Handler.post的关系

到此这篇关于Android中View.post和Handler.post的关系的文章就介绍到这了,更多相关View.post与Handler.post内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
详解Java分布式事务的 6 种解决方案
Jun 26 Java/Android
java设计模式--七大原则详解
Jul 21 Java/Android
Java使用JMeter进行高并发测试
Nov 23 Java/Android
Java异常处理try catch的基本用法
Dec 06 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 Java/Android
使用Java去实现超市会员管理系统
Mar 18 Java/Android
Netty分布式客户端接入流程初始化源码分析
Mar 25 Java/Android
Dubbo+zookeeper搭配分布式服务的过程详解
Apr 03 Java/Android
Android开发 使用文件储存的方式保存QQ密码
Apr 24 Java/Android
带你了解Java中的ForkJoin
Apr 28 Java/Android
Java数据结构之堆(优先队列)
May 20 Java/Android
Android开发手册TextInputLayout样式使用示例
Jun 10 Java/Android
Android Canvas绘制文字横纵向对齐
Jun 05 #Java/Android
Android 中的类文件和类加载器详情
Android中的Launch Mode详情
Spring中的@Transactional的工作原理
Jun 05 #Java/Android
Qt数据库应用之实现图片转pdf
Ubuntu18.04下QT开发Android无法连接设备问题解决实现
Jun 01 #Java/Android
openGauss数据库JDBC环境连接配置的详细过程(Eclipse)
Jun 01 #Java/Android
You might like
在“咖啡之国”感受咖啡文化
2021/03/03 咖啡文化
php数组函数序列之array_push() 数组尾部添加一个或多个元素(入栈),返回新长度。
2011/11/07 PHP
遍历指定目录下的所有目录和文件的php代码
2011/11/27 PHP
输入值/表单提交参数过滤有效防止sql注入的方法
2013/12/25 PHP
php使用指定字符列表生成随机字符串的方法
2015/04/18 PHP
php实现scws中文分词搜索的方法
2015/12/25 PHP
PHP的时间戳与具体时间转化的简单实现
2016/06/13 PHP
[原创]php简单隔行变色功能实现代码
2016/07/09 PHP
tp5(thinkPHP5框架)使用DB实现批量删除功能示例
2019/05/28 PHP
jquery 获取json数据实现代码
2009/04/27 Javascript
jquery imgareaselect 使用利用js与程序结合实现图片剪切
2009/07/30 Javascript
Javascript的并行运算实现代码
2010/11/19 Javascript
JS中for循序中延迟加载动态效果的具体实现
2013/08/18 Javascript
JavaScript实现页面5秒后自动跳转的方法
2015/04/16 Javascript
jQuery无刷新切换主题皮肤实例讲解
2015/10/21 Javascript
HTML5 JS压缩图片并获取图片BASE64编码上传
2020/11/16 Javascript
JS简单实现移动端日历功能示例
2016/12/28 Javascript
JavaScript关联数组用法分析【概念、定义、遍历】
2017/03/15 Javascript
ReactNative之键盘Keyboard的弹出与消失示例
2017/07/11 Javascript
记录微信小程序 height: calc(xx - xx);无效问题
2019/12/30 Javascript
Vue实现剪切板图片压缩功能
2020/02/04 Javascript
[02:33]DOTA2英雄基础教程 司夜刺客
2013/12/04 DOTA
[01:12]快闪回顾DOTA2亚洲邀请赛(DAC) 静候2018新征程开启
2018/03/11 DOTA
Python数据结构之Array用法实例
2014/10/09 Python
Python字符串处理之count()方法的使用
2015/05/18 Python
python 移动图片到另外一个文件夹的实例
2019/01/10 Python
Html5 localStorage入门教程
2018/04/26 HTML / CSS
Regatta官网:英国最受欢迎的户外服装和鞋类品牌
2019/05/01 全球购物
初一家长会邀请函
2014/01/31 职场文书
分公司经理任命书
2014/06/05 职场文书
关爱残疾人标语
2014/06/25 职场文书
合作经营协议书范本
2014/09/16 职场文书
财务工作犯错检讨书
2014/10/07 职场文书
2015年毕业实习工作总结
2015/05/29 职场文书
财务管理制度范本
2015/08/04 职场文书
四年级语文教学反思
2016/03/03 职场文书