源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

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

  • 时间:2020-12-16 07:13 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:View.post() 不靠谱的地方你知道多少
[b]一、前言[/b] 有时候,我们会需要用到 [code]View.post() [/code]方法,来将一个 Runnable 发送到主线程去执行。这一切,看似很美好,它最终会通过一个 [code]Handler.post()[/code] 方法去执行,又避免我们重新定义一个 Handler 对象。 但是,从 Android 7.0(Api level 24) 开始,View.post() 将不再那么靠谱了,你 post() 出去的 Runnable ,可能永远也不会有机会执行到。 [b]二、post 在 7.0 的差异[/b] [b]2.1 post 方法的差异[/b] 前面提到,这个问题只出现在 Android 7.0 上。那么就先从源码分析 Android 7.0 到底对 View.post() 做了什么改动。 [img]http://files.jb51.net/file_images/article/201708/2017082915025118.jpg[/img] 用 Diff 看一下它们的差异,左边是 Api Level 24+(以下简称 Api24) 的代码,右边是 Api level 23-(以下简称 Api23) 的代码。 很明显的可以看出来,它们只有在 [code]mAttachInfo[/code] 为 null 的时候,执行的逻辑才会有差异。 Api24 中,会调用 [code]getRunQueue().post(action)[/code],而 Api23 会调用[code] ViewRootImpl.getRunQueue().post(action) [/code]方法,他们的差异就在这里。 [b]2.2 Api23 post 的细节[/b] 先简单理解一下,[code]ViewRootImpl [/code]是什么。 [code]ViewRootImpl [/code]可以理解是一个 Activity 的 ViewTree 的根节点的实例。每个 ViewRootImpl 就是用来管理 DecorView 和 ViewTree。 [code]ViewRootImpl [/code]中的用来承载 Runnable 的队列是 sRunQueues ,它一个静态的变量,也就是说在 App 的生命周期内,[code]ViewRootImpl [/code]中的这个消息队列都是同一个。 再来看看前面提到的 [code]ViewRootImpl.getRunQueue().post() [/code]到底干了什么? [img]http://files.jb51.net/file_images/article/201708/2017082915025119.jpg[/img] [code]post() [/code]方法只是单纯的将它包装成一个 HandlerAction 对象,然后放入 mActions 这个 ArrayList 中。继续追查下去就需要知道 mActions 中添加的 HandlerAction 在何时被消费掉了。 消费 HandlerAction 的地方,是 [code]executeActions() [/code]方法。 [img]http://files.jb51.net/file_images/article/201708/2017082915025120.jpg?201772915627[/img] 它最终,还是调用的 [code]handler.postDelayed() [/code],这没什么好说的,关键点在于[code] executeAction() [/code]方法,是在什么时候被调用的。 [code]executeAction() [/code]是被 TraversalRunnable 调用 [code]doTraversa()[/code] ,在[code]doTraversa() [/code]方法中,进行调用的。而 TraversalRunnable 又是通过 [code]Choreographer.postCallBack() [/code]去循环调用的。这个[code] Choreographer [/code]通过 [code]doScheduleCallback()[/code] 发送一个 [code]MSG_DO_SCHEDULE_CALLBACK [/code]类型的消息循环调用,间隔就是一个 VSync 的间隔。 关于 Choreographer ,不是本文的重点,有兴趣可以单独了解一下。 所以,在 Api23 以下,[code]executeAction()[/code] 是会被循环调用,基本上其内的 mActions 只要有未执行的 Runnable 立刻就会被消费掉。 所以在 Api23 以下的设备上,View.post() 基本上是靠谱的,post 出去的 Runnable 都会有机会执行到。 [b]2.3 Api24 的细节[/b] 再来看看在 Api24 中的实现细节,在 Api24 中,调用的是[code] getRunQueue().post() [/code]方法,它操作的是一个 [code]HandlerActionQueue[/code] 对象。 [img]http://files.jb51.net/file_images/article/201708/2017082915025121.jpg[/img] 内部的结构其实和 Api23 很像,也是维护了一个 HandlerAction 的数组 mActions 。 最终消费掉 mActions 的地方,依然是一个 [code]executeActions() [/code]方法。 [img]http://files.jb51.net/file_images/article/201708/2017082915025122.jpg?20177291575[/img] 回到根本的问题,[code]executeActions() [/code]方法在什么时机会被调用到,继续追查可以看到它在 [code]View.dispatchAttachedToWindow() [/code]方法中,会被调用。 [img]http://files.jb51.net/file_images/article/201708/2017082915025123.jpg[/img] 既然,exe[code]cuteActions()[/code] 方法,在 Api24 及以上,只会在 [code]dispatchAttachedToWindow() [/code]的方法中,才有机会被调用到,而 [code]View.dispatchAttachedToWindow() [/code]方法,只有在这个 View 通过 [code]addView() [/code]等方法,加入到一个 ViewGroup 的时候,才会被调用到。这就导致写在 Layout 布局中的控件,是不会有机会再调用 [code]addView() [/code]方法的,所以它永远也得不到执行。这也就到时了 Api24 下,[code]View.post() [/code]表现的现象不一致的缘故。 [b]三、小结[/b] [code]View.post() [/code]方法,在不同版本的差异,根本原因还是在于 Api23 和 Api24 中,[code]executeActions() [/code]方法的调用时机不同,导致 View 在没有 mAttachInfo 对象的时候,表现不一样了。 所以我们在使用的过程中需要慎用,区分出实际使用的场景,一般规范自己的代码即可: 在 View 已经被显示出来之后,再调用 [code]View.post() [/code]方法(这个时候 mAttachInfo 已经不为空了)。 尽量避免使用 [code]View.post() [/code]方法,可以直接使用 [code]Handler.post() [/code]方法来替代。 [b]总结[/b] 以上所述是小编给大家介绍的View.post() 不靠谱的地方,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程素材网网站的支持!
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部