每一个搞 Android 的对 Handler 应该都不陌生,网上大神的文章也是数不尽的。这篇文章是个人在学习 Handler 过程中的一些疑问的记录。
Handler 消息机制学习总结
1. 什么是 Handler,有什么作用
Handler 首先是一个 java 类,定义在/frameworks/base/core/java/android/os/Handler.java
。但一般提到 Handler 并不专指它自身,常用来代指 Android 中的消息处理机制,或者说线程间通信的工具,典型应用就是在子线程中更新 UI 线程。那么,很显然 Handler 的作用就是用于线程间通信。
2. Android 消息机制的四要素
Message:需要传递的消息,通信的过程不就是消息传递的过程嘛。
MessageQueue:消息队列是消息保存的容器,收到的消息会首先进入 MessageQueue 中,等待被取出后处理。
Handler:负责消息的发送和处理,可见 Handler 的重要地位。
Looper:负责消息分发,会不断尝试从 MessageQueue 中取出消息,交给对应的 Handler 来处理。
3. 四要素间是如何协调工作的
首先要知道以下几点:
- 主线程有且仅有一个 Looper,Looper 内部持有一个 MessageQueue 类对象,也就是说 MessageQueue 其实是封装在 Looper 内部的,也是唯一的。
- 一个 Looper 可以对应多个 Handler,负责在不同子线程中发送和处理消息,它们发送的消息都会到主线程中的 Messagequeue 中来,并最终被 Looper 派发到对应子线程中的 Handler 来处理。
- 一个 Handler 只能对应一个 looper。Handler 在创建的时候会绑定 Looper,如果没有指定就是当前线程对应的 Looper,handleMessage 的回调是在 Looper 所在的线程。
这里直接放一张郭神博客里 Handler 工作流程图。
我们以更新 UI 的场景为例,描述一下运行的过程。
- 首先在主线程中创建 Handler 对象 handler1,创建后该 Handler 就会与主线程的 Looper 绑定;
- 创建子线程,并在子线程中执行某些 UI 相关的任务,任务执行完后在子线程中调用 handler1.sendMessage,更新 UI 的消息 message1 随后会进入到主线程的 messageQueue 中;
- Looper 处理到 message1 时,会将该消息派发到对应的 Handler(handler1)进行处理,回调到它的 handleMessage 方法,完成 UI 的更新。
这个过程说起来很简单,但其中的一些细节还是比较让人困惑,我们接着一起看。
4. Handler 创建时是怎么绑定 Looper 的
我们不妨一起来看一下 Handler 这个类。它维护有一个 mLooper 和 mQueue,分别用来记录该 Handler 对应的 Looper 和 MessageQueue。
942 final Looper mLooper;
943 final MessageQueue mQueue;
下面看一下这两个量的赋值,也就是 Handler 的初始化方法。
//不指定 Looper
128 public Handler() {
129 this(null, false);
130 }
//指定 Looper
161 public Handler(@NonNull Looper looper) {
162 this(looper, null, false);
163 }
214 public Handler(@Nullable Callback callback, boolean async) {
//获取当前线程的 Looper 对象
224 mLooper = Looper.myLooper();
230 mQueue = mLooper.mQueue;
...
233 }
255 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
//使用指定的 looper
256 mLooper = looper;
257 mQueue = looper.mQueue;
260 }
从 Handler 的构造方法,我们不难发现。当未指定 Looper 时,Handler 将绑定创建它的线程的 Looper 对象;而如果指定了,mLooper 则赋值为指定的 Looper 对象。mQueue 始终都是绑定的 Looper 对应的 MessageQueue。
下面是 Looper.myLooper()
的定义
307 /**
308 * Return the Looper object associated with the current thread. Returns
309 * null if the calling thread is not associated with a Looper.
310 */
311 public static @Nullable Looper myLooper() {
312 return sThreadLocal.get();
313 }
5. 子线程中消息是怎么发送的
哈哈,当然是调用 Handler 的 send 和 post 相关方法啊。哦,说的有点概况了,具体调用哪个方法很灵活,但最终发送消息一定会调用到sendMessageAtTime()
。
719 public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
720 MessageQueue queue = mQueue;
721 if (queue == null) {
722 RuntimeException e = new RuntimeException(
723 this + " sendMessageAtTime() called with no mQueue");
724 Log.w("Looper", e.getMessage(), e);
725 return false;
726 }
727 return enqueueMessage(queue, msg, uptimeMillis);
728 }
729
这里指定 queue 是 mQueue 了,也就是 Looper 对应线程的 MessageQueue, 在 enqueueMessage()
方法中主要就是做入队操作了。留意一下 msg.target = this
,我们后面还会提到。
770 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
771 long uptimeMillis) {
772 msg.target = this;
773 msg.workSourceUid = ThreadLocalWorkSource.getUid();
774
775 if (mAsynchronous) {
776 msg.setAsynchronous(true);
777 }
778 return queue.enqueueMessage(msg, uptimeMillis);
779 }
6. Looper 是怎么派发消息到对应 Handler 的
Looper 在创建后,当调用了 Looper.loop()
就会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler,所以看一下 Looper.loop()
。
260 public static void loop() {
261 final Looper me = myLooper();
262 if (me == null) {
263 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
...
287 for (;;) {
288 if (!loopOnce(me, ident, thresholdOverride)) {
289 return;
290 }
291 }
292 }
155 /**
156 * Poll and deliver single message, return true if the outer loop should continue.
157 */
158 @SuppressWarnings("AndroidFrameworkBinderIdentity")
159 private static boolean loopOnce(final Looper me,
160 final long ident, final int thresholdOverride) {
161 Message msg = me.mQueue.next(); // might block
162 if (msg == null) {
163 // No message indicates that the message queue is quitting.
164 return false;
165 }
...
200 try {
//target 就是一个 Handler 类对象
201 msg.target.dispatchMessage(msg);
...
206 }
...
250 msg.recycleUnchecked();
251
252 return true;
253 }
在上一节我们就有说到 msg.target
,它是什么,我们到 Message.java 中一探究竟。
哦,原来就是一个 Handler 类对象,这就很容易理解了。每一个 Message 在发送的时候就赋值了 target 的值,在派发的时候就只需要根据 target 调用 dispatchMessage()
方法就行了。
97 public void dispatchMessage(@NonNull Message msg) {
98 if (msg.callback != null) {
99 handleCallback(msg);
100 } else {
101 if (mCallback != null) {
102 if (mCallback.handleMessage(msg)) {
103 return;
104 }
105 }
106 handleMessage(msg);
107 }
108 }
handleMessage()
就是我们在开始创建 Handler 时重写的方法,在这里被最终调用。
7. 如何保证一个线程只有一个 Looper
答案藏在 Looper 的 Looper.prepare()
中,也就是将当前线程初始化为 Looper 线程的过程中。
108 public static void prepare() {
109 prepare(true);
110 }
111
112 private static void prepare(boolean quitAllowed) {
113 if (sThreadLocal.get() != null) {
114 throw new RuntimeException("Only one Looper may be created per thread");
115 }
116 sThreadLocal.set(new Looper(quitAllowed));
117 }
除非调用过 prepare()
, 否则 sThreadLocal.get()
将会返回 null,这就保证了 Looper 的构造方法只会调用一次,保证 Looper 的唯一性。
70 // sThreadLocal.get() will return null unless you've called prepare().
71 @UnsupportedAppUsage
72 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这里还有个疑问,创建的 Looper 为何要保存到 ThreadLocal 中。那得先说说 ThreadLocal 了,它是线程内部的数据存储类(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。相应区域数据改变只与当前线程关联,线程之间互不干扰。这就能保证每个线程的 Looper 都是独立使用的。
以上就是我个人在学习 Handler 过程的主要疑问与心得了,欢迎大家一起讨论。最后贴上某位大神整理的 Handler 架构图,方便大家理解记忆。原图出处在此