Android-Handler
Android-Handler
1. Handler的实例化和初始化
Handler 在 Android 开发中还是相当相当常见的,其涉及到了很多 Android 和 Java 的数据结构。Handler 的源码并不复杂,原理也很有意思,常用的方法里实例化和初始化就占了很大一部分,消息 Message 的传递也占了很大一部分,本文主要也是分析这两点,剩下的用的不是太多,直接阅读源码也很快,在本文就不分析了。通过阅读 Handler 以及相关类的源码,可以深刻体会到 Android 的一些通用设计理念。
1.1 什么是Handler
在 Handler 的源码中,有一段很长的注释:
1 | /** |
大概翻译一下,这段注释基本也介绍清楚了 Handler 的工作流程:
- Handler 允许你发送和处理和线程的 MessageQueue 相关联的 Message 和 Runnable 对象。每个 Handler 实例对象都只和一个线程以及该线程的消息队列。当创建一个 Handler 实例时,这个 Handler 会与线程以及这个线程创建的消息队列绑定,这之后,这个 Handler 会负责给 MessageQueue 传送 Message 和 Runnable,或者负责处理从 MessageQueue 中取出的 Message 和 Runnable。
- Handler 有 2 个主要的用途:(1)给 Message 和 Runnable 安排一个未来执行的时间(可以是相差 0,表示立即执行);(2)将一个事务提交到另一个不同的线程处理。
- Handler 通过
post
,postAtTime(Runnable, long)
,postDelayed
,sendEmptyMessage
,sendMessage
,sendMessageAtTime
,sendMessageDelayed
方法调度 Message。post
方式允许发送一个 Runnable 对象并在被 MessageQueue 接收后调用。shendMessage
方式允许发送一个包括了一个 Bundle 形式的数据的 Message,并且在Handler.handleMessage()
方法中处理(要求实现一个 Handler 的子类)。 - 不论使用
post
还是send
方式发送消息,都可以让这些消息在 MessageQueue 可用时立即处理,或者指定一个延时间隔来延迟处理,或者指定一个具体的处理事件。后面二者可以通过:延迟、具体时间、或其他基于时间的行为来实现。 - 当 App 的进程被创建时,它的主线程则专门用来运行一个管理顶层应用对象的 MessageQueue,这些顶层应用对象可以是例如:Activity,BroadcastReceiver 等等,或是他们创建的其他 Windows。你可以创建自己的子线程,并通过 Handler 与主线程通信,只需要在子线程中调用上述的
send
类型或post
类型的方法即可,发送的 Message 或 Runnable 将在 Handler 的 MessageQueue 合适的时候被调度和处理。
需要注意的几个重点:(1)Handler 可以用来发送消息也可以用来处理消息;(2)发送消息有两种类型的方式,处理消息可以跨进程;(3)Handler 的实例是和当前线程以及当前线程的 MessageQueue 绑定的;(4)MessageQueue 中维护的单链表只支持 Message 对象,因此 Runnable 是通过封装进 Message 来实现的。
1.2 Handler构造方法
Handler 内部有多个不同参数的构造方法,依次看看:
(1)无参默认构造方法
1 | /** |
该构造方法会使得 Handler 与当前线程的 Looper 关联,如果当前线程没有 Looper,则 Handler 将无法接收消息,从而抛出异常。
(2)1 个参数(Callback
)的构造方法
1 | /** |
和默认无参构造方法类似,只不过重写了接收消息后处理的回调方法 handleMessage
。
(3)1 个参数(async
)的构造方法
1 | /** |
和默认无参构造方法类似,只不过指定了通过这个 Handler 发送的消息为异步消息(Async)。
(4)以上第 1、2、3 种构造方法最终均调用了 2 个参数(Callback, async
)的构造方法:
1 | /** |
Looper.myLooper()
就是返回了当前线程中的 Looper 实例对象,如果当前线程不具备处理消息的功能,也即没有 Looper 实例,则返回 null
,接下来也就是当存在 Looper 实例对象时,获取并绑定其消息队列 MessageQueue,处理事件的回调 Callback,以及设置是否异步 async。
(5)1 个参数(Looper
)的构造方法
1 | /** |
手动指定这个 Handler 需要绑定的线程,当我们需要从子线程更新 UI 时,通常会使用如下方法来实例化一个 Handler:
1 | Handler handler = new Handler(Looper.getMainLooper()); |
这其实就是调用的这个构造方法,而 Looper.getMainLooper()
返回的就是主线程的 Looper 实例对象。
(6)2 个参数(Looper, Callback
)的构造方法
1 | /** |
同时指定 Looper 和 Callback。
(7)以上第 5、6 种构造方法最终均调用了 3 个参数的构造方法:
1 | /** |
这个构造方法则是最详细的,可以同时指定 Looper,Callback,以及是否异步 async。
通过查看以上 7 种构造方法,可以总结出 Handler 的实例化过程:如果没有显式传入任何参数,则默认绑定当前线程的 Looper,使用默认 Callback,发送同步消息,否则按照对应显式传入的参数手动指定。
2. Handler发送Message
2.1 源码分析
在平时开发中 Handler 最主要的用途就是在子线程中更新 UI,两种方式:
send
方式,包括:sendMessage
或sendEmptyMessage
sendMessageDelayed
或sendEmptyMessageDelayed
sendMessageAtTime
或sendEmptyMessageAtTime
sendMessageAtFrontOfQueue
(比较特殊,会在后面详细讲解)
post
方式,包括:post
postDelayed
postAtTime
postAtFrontOfQueue
其中,通过查看 post
方式的源码可以发现:
1 | // 默认 |
实际上 post
方式最终都是通过 send
方式实现的。再看一下各个 send
方式的源码:
1 | public final boolean sendMessage( Message msg){ |
在以上源码中可以看到一个参数:uptimeMillis
,这个参数是从开机到当前时刻的时间间隔(毫秒,当设备处于睡眠时不计算),表示消息需要被处理时的时间,任意不同时刻获取到的该值都是不同的,uptimeMillis
越小表示消息越早处理。消息在添加到消息队列中时,是按照实际处理的时间从先到后按顺序排列的,并且除了 sendMessageAtFrontOfQueue
方法之外,其他 send
方式本质都是通过这一个方法发送的:sendMessageAtTime
,而这两个方法最终都调用了 enqueueMessage
,看看这个方法内部:
1 | private boolean enqueueMessage(long uptimeMillis) MessageQueue queue, Message msg, { |
这个方法每一句都说明了 Message 的一个性质:
msg.target = this;
这句把 Message 和 Handler 绑定在了一起。msg.workSourceUid = ThreadLocalWorkSource.getUid();
这句是获取到了当前线程的 Uid,也就是和当前线程绑定在了一起。msg.setAsynchronous(true);
这句是设置为异步,if
也表示默认情况下的 Message 是同步的。
最后调用了 MessageQueue.enqueueMessage
方法把 Message 插入到消息队列中。
2.2 流程总结
通过以上源码分析,Handler 在发送一个消息后的流程也就清楚了:
- 接收一个 Message,并根据设置的延时,设置 Message 的
when
- 根据情况设置 Message 的类型,默认情况下为同步消息
- 将 Message 的
target
设置为自己 - 调用 MessageQueue 的
enqueueMessage
方法 MessageQueue.enqueueMessage
方法中,根据when
从小到大的顺序将 Message 插入到单链表对应位置- 根据条件唤醒 Native 层的消息队列
3. Handler机制总结
通过分析 Handler 的实例化和初始化,以及 Handler 发送消息的流程,可以总结出 Handler 的工作机制:
- Handler 默认和当前线程以及当前线程的 MessageQueue 绑定,默认发送同步消息。
- 可以手动指定 Handler 的线程、重写接收消息后的回调方法
Callback.handleMessage()
。 - 可以手动指定发送的消息类型,但一个 Handler 只能发送一种消息,不能交替发送。
- Handler 所在线程由实例化时的 Looper 有关,且只能接收到与之绑定的线程中的消息,如果线程不具备处理消息的功能,则 Handler 无法接收消息且会抛出异常。
- Handler 有两种发送消息的方式,但本质上都是
send
方式。 - Handler 可以发送 Message,也可以发送 Runnable,但由于 MessageQueue 维护的单链表元素是 Message,因此 Runnable 实际上是封装在 message 中的。