写在前面:一名有三年Android开发经验的女程序员(欢迎大家关注我 ~期待和大家一起交流和学习Android的相关知识)
相信大家早就听说过了 Looper 以及 MessageQueue 了,我就不多绕弯子了。 不过在开始分析原理之前,先明确要谈论的问题:
-
Handler 是如何与线程关联的?
-
Handler 发出去的消息是谁管理的?
-
消息又是怎么回到 handleMessage() 方法的?
-
线程的切换是怎么回事?
一、Handler 与 Looper 的关联
实际上我们在实例化 Handler 的时候 Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,也就是说在创建 Handler 之前一定需要先创建 Looper 。 代码如下:

这个异常相信很多同学遇到过,而我们平时直接使用感受不到这个异常是因为主线程已经为我们创建好了 Looper。一个完整的 Handler 使用例子其实是这样的:

Looper.prepare() :

Looper 提供了 Looper.prepare() 方法来创建 Looper ,并且会借助 ThreadLocal 来实现与当前线程的绑定功能。Looper.loop() 则会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler(见【三】)。 也就是说 Handler 跟线程的关联是靠 Looper 来实现的。

二、Message 的存储与管理
Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。 不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。 以 sendEmptyMessage(int) 方法为例:

三、Message 的分发与处理
了解清楚 Message 的发送与存储管理后,就该揭开分发与处理的面纱了。前面说到了 Looper.loop() 负责对消息的分发,本章节进行分析。先来看看所涉及到的方法:

loop()里调用了MessageQueue.next():

还调用了 msg.target.dispatchMessage(msg) ,msg.target 就是发送该消息的 Handler,这样就回调到了 Handler 那边去了:

注意:dispatchMessage() 方法针对 Runnable 的方法做了特殊处理,如果是 ,则会直接执行 Runnable.run() 。 分析:Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg) 回到了 Handler 来分发消息,以此来完成消息的回调。
那么线程的切换又是怎么回事呢? 很多人搞不懂这个原理,但是其实非常简单,我们将所涉及的方法调用栈画出来,如下:

显而易见,Handler.handleMessage() 所在的线程最终由调用 Looper.loop() 的线程所决定。 平时我们用的时候从异步线程发送消息到 Handler,这个 Handler 的 handleMessage() 方法是在主线程调用的,所以消息就从异步线程切换到了主线程。
三、图解原理


小结
Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。 尝试小结一下它们的职责,如下:
-
Looper :负责关联线程以及消息的分发在该线程下**从 MessageQueue 获取 Message,分发给 Handler ;
-
MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
-
Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。
Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。 线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

每天和大家分享和程序员有关的文章,促进大家一起学习和交流,油吧程序员!
网友评论