90

runOnUiThread 、Handler.post、View.post 有什么区别?(解决篇)

 6 years ago
source link: https://mp.weixin.qq.com/s?__biz=MjM5OTE4ODgzMw%3D%3D&mid=2247483722&idx=1&sn=7290f6eefc0ef19d933c0b4039865bcb&chksm=a73e01449049885220395c4906f6293900a7bb51fc2f509643839f3c41440a51ddae01d9eb56&mpshare=1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

runOnUiThread 、Handler.post、View.post 有什么区别?(解决篇)

Original 巴掌 巴巴巴掌 2017-12-12 23:30 Posted on

该文章已经收集到面试题整理(可在首页点击底部 Tab 看到)。

在上篇公众号文章中,我提了一个问题,如下图:

Image

想得到答案,就得知道 runOnUiThread 、Handler.post、View.post 三者的区别。

从难易程度来讲,我们先说下 runOnUiThread 和 Handler.post 的区别,先看看 runOnUiThread 的源码:

如果当前不是 UI 线程,那么由主线程的 Handler 扔个消息给 MessageQueue;如果当前是 UI 线程,则立刻执行。

知道这样的话,之前的那个题目就能回答一部分了,【234】的顺序应该是【324】,因为第一个 runOnUiThread 会立刻执行,而【24】就依据进入 MessageQueue 的先后顺序执行。

相信给【234】排序应该大部分人都错不了,那么第一个 View.post 呢?这个就略坑了。

题目设了个陷阱大家应该都看到了,View 实例化后并没有被加进任何能 attachToWindow 的 ViewGroup 中,这是一个很大的坑点。

我们先来看看 View.post 的源码,值得注意的是,View.post 的实现从 api24 开始有了变化,也就是说文章最开始的那道题在 Android 7.0(api24)和 7.0 以下的设备上输出的答案会不同。

先看看 api24 上的源码:

再看看 api23 上的源码:

看到区别了吧,当 mAttachInfo 为 null 的时候,源码变了。

我们先分析下相同部分的代码,也就是当 mAttachInfo 不为 null 的情况,我们看看它是啥时候被赋值的:

View#dispatchAttachedToWindow

再看看 View 的dispatchAttachedToWindow 是哪位哥哥调的,显然是 ViewGroup:

ViewGroup#dispatchAttachedToWindow

那么这个 ViewGroup 是谁?又是谁调用的?

先来看看是谁调用的:

ViewRootImpl#performTraversals

调用方知道了,那么 host 是谁?host 其实就是 Activity 的 DecorView,再往下就不在这篇文章里扯了,我们只要知道,要想让子 View 能调用 dispatchAttachedToWindow,那么一定得是 DecorView 的子 View,而我们【1】中创建的 View 并不是 DecorView 的子 View,因此【1】中的 View 的 dispatchAttachedToWindow 方法并不会被执行到,所以最开始说的 mAttachInfo 在我们这道题里是为 null 的。

理了半天,只是把 api23 和 api23 以上相同部分的源码解释了下,那么不相同的源码呢?也就是当 mAttachInfo 为 null 的情况,别急,慢慢道来。

上面说了,我们【1】的例子就会使 mAttachInfo 为 null,在 api23 及以下就会执行  ViewRootImpl.getRunQueue().post(action);  而 api24 开始会执行 getRunQueue().post(action); 。

来看看两者的区别,api23 中是使用的 ViewRootImpl 的 RunQueue:

sRunQueues 是个静态的 ThreadLocal 对象,关于 ThreadLocal 也是面试常问题,之后单开文章讲解,这里要知道的就是,主线程用的是同一个 RunQueue,这个 RunQueue 里的 Runnable 啥时候会执行?

ViewRootImpl#performTraversals

performTraversals 方法是整个 View 的绘制流程的开始,走到这里时,api23 及一下的 View.post 代码会执行,也就是题目中的答案在 api23 及以下设备会是【3241】,那么 api24 以后呢?直接使用 View 里的 getRunQueue:

Image

他已经不是使用的主线程 RunQueue,而是自己这个对象里的,那自己的 RunQueue 啥时候执行呢?之前说了,在 dispatchAttachedToWindow 里会执行到,而【1】的 View 并不会执行 dispatchAttachedToWindow,也就是说在 api24 开始题目的答案是 【324】。

全部分析完了,正确答案应该是【324】/【3241】,需要区分 api 版本。有不少朋友留言给出了自己的答案,感谢大家的参与,希望这个分析对大家能有帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK