看下面例子:
在子线程发送一个消息,然后在主线程街道这个消息处理,这个消息是如何从子线程切换到主线程的呢?首先跟踪一下handler.sendMessage(new Message())如下:
image.png
image.png
从上面两图看辗转来到sendMessageAtTime方法,看到A处,此处mQueue是何方神圣呢???看名字只是知道个大概是个消息队列, 而B处看方法名都知道是将msg入队列了:
mQueue在源码中两处被赋值,都是在构造方法里:
image.png
image.png
本文这个例子创建Handler对象的时候调用的是其无参的构造方法,如下:
image.png
正是上上图C处所在的构造函数,从C、D处可以看到Handler对象的MessageQueue是looper对象的成员mQueue赋予的,而looper对象又是如何创建的呢?
image.png
嗯,这里我简单的解释一下ThreadLocall,表示每一个线程都有自己的一份备份,相互独立。而主线程是什么时候设置这个ThreadLocal的呢??其实在我之前的文章Activity生命周期回调是如何被回调的?中源码分析的时候已经有涉及到这一点: image.png
如上图E1处:
image.png
F处又跳转调用了prepare方法:
image.png
这里注意一下咯:Looper构造函数是私有的,在类外部是没办法直接new设置给一个线程的,不过也不用担心,调用他的prepare这个静态方法就可以了,所以这里记 住哦,Looper的prepare方法很重要,可以给一个线程“挂载”一个Looper对象,好啦回到F处,接下来看下标记G处呗:
image.png
接下来看到上图中的E2处调用了Looper.loop方法:
image.png
image.png
从上面两张图可以看到,Looper.loop方法就是进入一个死循环,在循环体内不断地从队列取出消息,然后进行分发处理见标记E3处,嗯,上图你可以看到有一个E4,这里计算了消息分发耗时,其实这里有一个应用,可以利用这里点计算UI线程是否卡顿,像这个问题的文章已经是烂大街了,随便百度一下都有,嗯,有时间我可能也会写一篇总结一下,回到上面提到的标记E3处:
msg.target.dispatchMessage(msg);我们知道这个msg是Message对象,而这个target呢??它其实是一个Handler对象,问题是这个target又是什么时候被赋值的??好吧,你暂且先记下这个疑问待会再说咯:
image.png
好的,到目前为止你已经知道消息是如何派发哪里被处理的了,继续往回翻,回到标记C处:
image.png所以C处只是取出当前线程“挂载”的Looper对象而已,接下去看D,“mLooper.mQueue”这分明是说mQueue是mLooper的成员呀,进去Looper内部研究研究成员结构:
image.png
所以到这里你已经了解以下这些事实:
- Looper对象可以通过静态方法prepare“挂载”一个线程上
- Handler对象内部用到的Looper、MessageQueue都是来自当前线程“挂载”的Looper(顺便提醒一下,这个事实就说明了一个线程要使用Handler之前要先让这个线程“挂载”上一个Looper对象,哈哈,那又怎么让线程“挂载”上一个Looper呢?上文不是说了吗——调用prepare静态函数)
- Looper的loop方法会启动一个死循环不断的从队列中读取消息,然后将消息送到Handler对象的handleMessage方法中处理
嗯,现在可以回到文章开头的标记B处,这里可以看到当调用Handler对象来发送消息时,会调用enqueueMessage方法,将消息入队列:
image.png
见上图,标记 I 处是真正的将消息入队列,不过在入队列之前多做了一项操作见标记H处——将handler对象设置到msg消息对象的target成员中,到此上文那个叫你暂时存档的疑问也帮你解决了。
按国际惯例来个总结咯:
-
一个线程在使用Handler之前要先在线程上挂载一个Looper,android主线程在ActivityThread的main方法已经做了这个挂载步骤,所以平时我们在主线程之间创建Handler对象式直接可以使用的不会出错,但是在子线程创建Handler对象就必须先帮线程挂在一个Looper对象。
-
Looper的loop方法会开启一个死循环不断读取消息队列的消息,然后传给handler对象的handleMessage方法处理
-
handler对象在发送消息的时候,会将消息入消息队列,压入队列之前会将自身引用设置在Message消息对象的target成员方便loop死循环读取消息之后分发消息。
网友评论