Android - Handler 消息机制
一. 概念
1. Handler
个人理解:
- 消息辅助类,Handler 是主线程和子线程沟通的桥梁,可以通过以 send / post 为前缀的一些方法(例如:sendMessage、postDelayed)发送消息(Message)到消息队列(MessageQueue);也可以处理消息循环器(Looper)分发过来的消息(Message),轻松的将一个任务切换到 Handler 所在的线程中执行。Android不允许在子线程中更新UI,在子线程执行耗时操作后,通过Handler将更新UI的操作切换到主线程执行。
Google官方文档的介绍(我的蹩脚翻译):
- 通过 Handler 可以发送和处理 Message / Runnable 对象,Message 和 Runnable 跟线程的MessageQueue 相关。每个 Handler 实例都关联一个线程(Thread)和该线程的消息队列(MessageQueue)。创建Handler的同时会绑定到 Looper 。Looper 可以在 MessageQueue 里 添加 /取出 Message / Runnable ,并且 Looper 是运行在创建Handler的线程中。
- Handler 有两个主要的用途:
- 使 Message / Runnable 在未来的某个时间点得到处理。
- 处理不同线程的队列消息
- Handler可以通过
post(Runnable),postAtTime(java.lang.Runnable, long),postDelayed(Runnable, Object, long),sendEmptyMessage(int),sendMessage(Message),sendMessageAtTime(Message, long), andsendMessageDelayed(Message, long)发送消息。以 post 版本的方法是将 Runnable 对象封装成Message添加到消息队列,当Handler接收到消息时,会回调Runnable的run()方法;sendMessage 版本的方法将携带一些信息的Message对象添加到消息队列,当接收消息时,会回调handleMessage(Message)方法,这个方法在你创建Handler的时候由自己来重写。
2. Message
个人理解:
- 存储通信数据信息,是Handler发送、处理的消息对象。用于在主线程和子线程中传递。在子线程中将各种需要更新到UI的数据信息封装到Message中,然后通过Handle在主线程中解析出Message的信息,更新到UI。
Google官方文档的介绍(我的翻译):
Message 可用于发送给 Handler ,它包含三种int类型(what、arg1、arg2)和一个对象(obj)字段,不需要你自己再添加配置字段。
☆ 虽然 Message 的构造方法是 public 的,但是最高效的方式是通过调用 Message.obtain() 或者 Handler#obtainMessage 方法获得 Message 对象, 这种方式是通过对象池来获取 Message 。
3. MessageQueue
个人理解:
- 消息队列, 消息队列的主要功能是向消息池发送消息(
MessageQueue.enqueueMessage())和取走消息池的消息(MessageQueue.next());尽管MessageQueue叫消息队列,但是它的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构(即Message)来维护消息列表,单链表在插入和删除上比较有优势。
Google官方文档的介绍(我的翻译):
- 一个简单的类,持有 Looper 分发的消息列表。Message 不是直接添加到 MessageQueue 中,而是通过跟Looper相关的Handler对象。
- 可以通过 Looper.myQueue() 获取当前线程的MessageQueue。
4. Looper :
个人理解:
- 消息循环器,通过 Looper.loop() 负责在消息队列(MessageQueue)循环取出消息(Message),并且分发给对应的处理者(Handler)。是消息队列(MessageQueue)与处理者(Handler)的通信媒介。每个线程只能拥有一个 Looper ;一个 Looper 可以绑定多个线程的 Handler;所以多个线程通过Handler 发送 Message 时,是往同1个 Looper 所持有的 MessageQueue 中发送消息,提供了线程间通信的可能。
Google 官方文档的介绍(我的翻译):
Looper这个类用于在线程中进行消息循环。默认创建的子线程没有 Looper,如果创建的话,在线程中调用 prepare() ,然后调用 loop() 开启消息循环。
大部分与消息循环器(Looper)的交互是通过 Handler 类。
下面是一个 Looper/Thread 的典型例子,通过 prepare() 和 loop() ,创建 Handler 和Looper交互。
1 | class LooperThread extends Thread { |
二. 源码解读:
从一个普通的例子说起:
1 | //1.在主线程以匿名内部类的方式创建Handler对象 |
分析1:
1 | /** |
问题:上述1.2 的Looper对象是怎么来的呢?
答:分两种情况:
如果在主线程中创建Handler。在 Android 应用进程启动时,会默认创建 1 个主线程 ActivityThread(Android的主线程就是 ActivityThread ,也叫UI线程),主线程的入口方法为 main() ,在 main() 方法中系统会通过 Looper.prepareMainLooper() 来创建主线程的 Looper 以及 MessageQueue ,并通过 Looper.loop() 来开启主线程的消息循环。这个过程就是上面代码所示。
如果在子线程中创建Handler。则必须为当前线程创建 Looper,所以需要手动调用Looper.prepare() 创建Looper,然后调用 loop() 开启消息循环。
1 | public final class ActivityThread extends ClientTransactionHandler { |
分析2、分析3:
1 | /** |
分析4:
1 | public final class MessageQueue { |
分析5 :
1 | public class Handler { |
至此,消息循环、分发的过程分析完毕,总结一下:
通过 Looper.loop() 里的 for 循环来不断轮询消息;
通过 for 循环里的 MessageQueue.next() 方法获取下一条要处理的消息;
MessageQueue 的 next() 方法去获取消息队列里的消息;
成功获取到一条消息,则通过Message的target属性(即Handler)调用dispatchMessage(msg)方法处理消息,在该方法里又有三种不同的消息处理回调方式(代码里可以看出是有3种优先级顺序的),回调方式因创建Handler和发送消息方式的不同而不同。
如果消息的触发时间大于当前时间,则在next()方法里阻塞,阻塞时间根据创建消息时设定的延迟时间决定;这里涉及到native的方法,有利于节省CPU的资源。
下一步,我们继续分析消息的发送、入队过程:
在子线程中通过Handler发送消息到消息队列中 :
- send/post 发送消息的各种方法,最终都是调用 MessageQueue.enqueueMessage()方法
send方法:
1 | public class Handler { |
post方法:
1 | public class Handler { |
开始分析 MessageQueue.enqueueMessage() 方法
1 | public final class MessageQueue { |
至此,消息入队的过程分析完毕,总结一下:
- Message入队是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
下面,我们继续分析创建消息(Message)对象的最佳方式:
1 | //创建消息对象 |
以上获取Message实例的过程总结为:
- 如果消息池不是空的,从消息池(链表结构)的头部取走一个Message,并将链表的头部更新为取走消息的next(即下一条Message)。否则新建Message对象。
下面分析recycle(即Message的回收):
- 其中在Looper.loop()方法取出一条消息交给Handler处理后,有一个回收Message到消息池的操作,调用的是recycleUnchecked():
1 | public final class Message implements Parcelable { |
以上总结为:为了提高效率,减少对象频繁的创建和回收过程,提供了一个消息缓存队列。消息池的默认大小是50,当需要回收消息时(比如:分发消息给Handler处理后),将消息的属性参数清空,然后将回收的消息添加到消息池的头部。
至此,Android的消息机制分析完毕,Android的消息机制源码远没有这么简单,以上仅从Java层进行了分析,对于Native层的分析没有涉及到。希望大家一起加油,踏踏实实分析源码的脉络,遇到同样的问题,做到胸有成竹。也欢迎在留言区一起讨论,一起成长!
如果想及时收到我的文章更新,可关注以下公众号。

参考资料:
1.https://developer.android.google.cn/reference/android/os/Handler?hl=en
2.http://gityuan.com/2015/12/26/handler-message-framework/
3 .https://www.jianshu.com/p/b4d745c7ff7a
4.Android开发艺术探索第十章