认识Handler类
首先看一下Handler源码的官方介绍:
A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
简单翻译一下:
Handler
允许你发送和处理与线程的MessageQueue
相关的Message/Runnable
对象,每个Handler
实例都与一个线程及该线程的MessageQueue
关联。当你创建一个新的Handler
,它就会与创建它所在的线程及该线程的MessageQueue
关联起来。之后,Handler
就可以发送Message/Runnable
给MessageQueue
,并可以执行从MessageQueue
出来的Message/Runnable
。
Handler
主要有两种用途:1、规划在将来的某个时刻执行Message/Runnable
;2、在与自己不同的线程上执行操作。
从文档的介绍可以知道,与Handler的功能紧密相连的几个关键类有:
- MessageQueue 消息队列
- Message 消息对象
- Runnable 可运行对象
下面我们会一一熟悉这些类及其作用。
Handler的初始化
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
以上是Handler提供的初始化方法中的一部分,可以看到最终都会进入到
Handler(@Nullable Callback callback, boolean async)
Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
这两个方法里面去,那么我们来看下这两个方法的区别:
public Handler(@Nullable Callback callback, boolean async) {
··· ···
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
··· ···
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
··· ···
}
以上的省略号表示省略了一些代码,这里我们只关注重点。
可以看到,Handler
中有两个重要的参数需要在构造的时候初始化,一个是 mLooper
即 Looper
,一个是 mQueue
即 MessageQueue
对象,如果没有 Looper
,Handler
初始化的时候会直接抛出异常,而 MessageQueue
则直接从 Looper
获取。
既然 Looper
如此重要,我们不如先看一下 Looper
是什么。
Looper
看一下 Looper
源码的介绍:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.
简单翻译下:
Looper
用来为线程运行一个消息循环。线程默认没有关联的消息循环,要创建一个,需在该线程调用Looper.prepare()
方法,然后调用Looper.loop()
让它来处理消息直到循环被停止。
Looper的初始化
如文档里所介绍的,要创建一个 Looper
,需调用 Looper.prepare()
方法,那么我们来看下这个方法到底做了什么。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到Looper
初始化的时候创建了一个 MessageQueue
,而前面提到过的Handler
初始化的时候需要的MessageQueue
,就是在这里创建的。同时,Looper
初始化的时候还获取了当前线程。
注意:从
prepare()
方法可知,一个线程只能创建一个Looper
。
我们回到 Looper
源码介绍部分,一个线程里默认不存在 Looper
,要创建一个,需要调用 Looper.prepare()
方法,这个方法不但新建了一个 Looper
,同时还与当前线程关联起来,并且还新建了一个与之关联的 MessageQueue
。
Handler
源码介绍部分有这么一句话: 当你创建一个新的 Handler
,它就会与创建它所在的线程及该线程的 MessageQueue
关联起来。到这里我们应该已经看明白了它们是如何相互关联的。
Looper如何处理消息
文档介绍提到 Looper
通过 Looper.loop()
方法来处理消息,我们来看一下它在这里做了什么:
public static void loop() {
//获取当前线程的Looper
final Looper me = myLooper();
//如果没有则抛异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper里的MessageQueue
final MessageQueue queue = me.mQueue;
··· ···
//开启一个无限循环
for (;;) {
//从MessageQueue拿出Message
Message msg = queue.next(); // might block
··· ···
//派发Message
msg.target.dispatchMessage(msg);
··· ···
}
}
上面的代码缩略了很多细节,我们关注重点,调用 LooperLoop()
方法之后,Looper
开启了一个无限循环,从 MessageQueue
拿 Message
,拿到之后交给 Message
的 target
来处理。
那么 Message
里面的 target
是什么呢?Message
又是如何被放入 MessageQueue
中的呢?
我们接下来需要了解一下 MessageQueue
与 Message
。
MessageQueue
仍然是看官方文档介绍:
Low-level class holding the list of messages to be dispatched by a {@link Looper}. Messages are not added directly to a MessageQueue, but rather through {@link Handler} objects associated with the Looper.
翻译下:
MessageQueue
持有等待Looper
派发的Message
列表,Message
无法被直接添加到MessageQueue
,需要通过与Looper
关联的Handler
。
通过介绍我们知道了一些答案,即 Message
需要通过 Handler
才能被添加到 MessageQueue
中。那么我们找一下 MessageQueue
中接收 Message
的方法。
boolean enqueueMessage(Message msg, long when) {
//如果该消息没有target,抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//如果该消息正在处理中,抛出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//标记该消息正在处理中
msg.markInUse();
//消息的处理时间
msg.when = when;
Message p = mMessages;
//如果当前MessageQueue里没有Message,或者新加入的Message没有设置处理时间,
// 或者处理时间小于上一个Message的处理时间,那么就把消息放到表头
if (p == null || when == 0 || when < p.when{
msg.next = p;
mMessages = msg;
} else {
//如果当前MessageQueue中已有Message,且新加入的Message设置了处理时间,
//且处理时间大于上一个Message的处理时间,那么按照时间顺序把Message插入Message链表中,
//处理时间最早的Message放在表头,处理时间最晚的Message放在表尾
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
}
return true;
}
当然,上面的代码省略了很多,主要是事件队列唤醒机制相关的代码,这里不是我们关注的重点,就不详述了。
通过上面的代码我们可以知道,Message
必须要有 target
才能被加入到 MessageQueue
之中,并且,同一个 Message
不能被多次添加到MessageQueue
。Message
有一个参数 next
,它也是一个 Message
,可以证明 Message
是以链表结构来存储的。并且, Message
的顺序按照处理时间来排列,处理时间最晚的 Message
排在链表的尾部。
到目前为止,我们仍不知道 target
代表什么意思,那么我们需要来了解一下 Message
类。
Message
先看文档介绍:
Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases. While the constructor of Message is public, the best way to get one of these is to call {@link #obtain Message.obtain()} or one of the {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull them from a pool of recycled objects.
翻译下:
定义一条可以被发送到
Handler
,包含描述和任意数据对象的Message
。Message
包含两个额外的Int
字段和一个额外的Object
字段,以适应多种情况需求。虽然其构造方法是public
的,但构造Message
最佳的途径是通过Message.obtain()
方法或者Handler.obtainMessage()
系列方法,它们将从回收对象池中获取Message
。
简单来说 Message
主要是一个数据对象类,看一下 Message
中我们关心的几个字段:
public long when;
Handler target;
Message next;
when
是该消息需要被派发的时间,target
是一个 Handler
,next
是下一个 Message
的引用,表明 Message
是以链表方式存储的。
这里的 target
居然就是一个 Handler
。通过 MessageQueue
的介绍文档我们知道:Message
无法被直接添加到 MessageQueue
,需要通过与 Looper
相关连的 Handler
来添加。那么我们来看一下 Handler
是如何添加 Message
到 MessageQueue
中的,自然就能知道 taget
中的 Handler
是从何而来。
Handler发送消息
通过前面的 Handler
介绍,我们知道 Handler
可以发送 Message
和 Runnable
。那么我们去源码中找下它发送消息的方法:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
//target就是当前的Handler
msg.target = this;
··· ···
return queue.enqueueMessage(msg, uptimeMillis);
}
发送 Message
的主要方法为 sendMessage(Message msg)
,发送 Runnable
的主要方法为 post(Runnable r)
,但最终都进入到了 enqueueMessage
方法,并调用了 MessageQueue.enqueueMessage()
方法。
这里面值得注意的地方有两个:1、Runnable
最终被转换成了一个 Message
;2、Message
的 target
即是当前发送消息的 Handler
。
所以我们有了答案,Looper
的 loop()
方法里处理消息的 Handler
就是把消息加入到 MessageQueue
的 Handler
。
//派发Message
public static void loop() {
··· ···
msg.target.dispatchMessage(msg);
··· ···
}
所以 Handler
的文档介绍说:Handler
允许你发送和处理与线程的 MessageQueue
相关的 Message/Runnable
对象。
发送消息的是它,最终处理消息的也是它。只不过,Handler
与创建它所在的线程相关联,最终处理消息所在的线程即它所关联的线程,但它可以被其它线程用来发送消息,所以可以实现消息的线程切换处理。
Handler处理消息
前面介绍 Handler
、Message
的过程中其实忽略了处理消息相关的一些内容,这里作一个补充。
首先,Handler
有一个接收消息的方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) { }
继承了 Handler
类的子类需要实现该方法来接收消息。
另外,Handler
在初始化的时候可以传入一个 Callback
对象,该对象也是用于接收和处理消息的,如下:
/** Callback interface you can use when instantiating a
*Handler to avoid having to implement your own subclass of Handler.
*/
public interface Callback {
//return True if no further handling is desired
boolean handleMessage(@NonNull Message msg);
}
接口说明已经很清楚了,使用 Callback
接口可以避免必须实现自己的 Handler
子类。
最后,Message
对象中有一个 Runnable
字段:
Runnable callback;
它是在 Runnable
转换成 Message
的时候被包装进去的,转换方法如下:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
现在,我们来看一下 Handler
的消息处理方法 dispatchMessage(@NonNull Message msg)
的详细过程:
public void dispatchMessage(@NonNull Message msg) {
//如果Message的callback即Runnable不为空,就调用Runnable的run()方法来处理
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果初始化Handler时传入了Callback,则调用Callback.handleMessage方法来处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//调用Handler自身的handleMessage方法来处理
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
总结
Handler
在创建的时候就需要一个Looper
,如果创建时没有手动提供Looper
,那么Handler
会去拿当前线程的Looper
,如果当前线程没有Looper
,那么创建的时候就会抛出异常。Looper
可以使用Looper.prepare()
方法来创建,Looper
创建的同时会初始化并持有一个MessageQueue
,并且与当前创建它的线程关联。一个线程里最多只能有一个Looper
。MessageQueue
主要用来存取Message
,并且按照待派发时间先后顺序给Message
排好序。- 只有
Handler
可以往MessageQueue
中添加Message
。 Looper
在调用Looper.loop()
方法之后,会开启一个无限轮询去MessageQueue
中提取需要派发的Message
,提取到之后会调用Handler
的dispatchMessage
方法来处理该Message
。