这就是小伙伴们一般常用的两个用法。大家注意到了在第二个用法中出现了一个Looper.getMainLooper()
,使用它作为参数,即使MyHandler
是在子线程中定义的,但是它的handleMessage
方法依然运行在主线程。我们看一下这个参数究竟是什么东东~
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
可以看到这个Looper
就是我们上面传入的参数Looper.getMainLooper()
,也就说明了handleMessage
方法具体运行在哪个线程是和这个Looper
息息相关的。那么这个Looper
究竟是何方神圣,它是怎么做到线程切换的呢?
概述
我们先来看一张图
这就是整个Handler
在Java层的流程示意图。可以看到,在Handler
调用sendMessage
方法以后,Message
对象会被添加到MessageQueue
中去。而这个`Messag
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
eQueue就是被包裹在了
Looper中。那么
Looper对象是干什么的呢?它和
Handler`是什么关系呢?我们来看一下他们具体的职责把~
-
Handle
消息机制中作为一个对外暴露的工具,其内部包含了一个Looper
。负责Message
的发送及处理
Handler.sendMessage()
:向消息队列发送各种消息事件Handler.handleMessage()
:处理相应的消息事件
-
Looper
作为消息循环的核心,其内部包含了一个消息队列MessageQueue
,用于记录所有待处理的消息;通过Looper.loop()
不断地从MessageQueue
中抽取Message
,按分发机制将消息分发给目标处理者,可以看成是消息泵。注意,线程切换就是在这一步完成的。 -
MessageQueue
则作为一个消息队列,则包含了一系列链接在一起的Message
;不要被这个Queue的名字给迷惑了,就以为它是一个队列,但其实内部通过单链表的数据结构来维护消息列表,等待Looper
的抽取。 -
Message
则是消息体,内部又包含了一个目标处理器target
,这个target
正是最终处理它的Handler
哦?原来他们的职责是这样啊,可我还是不懂他们到底是怎么运行起来的。就像你告诉我医生是负责治病,警察是抓坏人的,他们具体是如何去做的呢?
Handler
从我们大家最熟悉的sendMessage
方法说起。sendMessage
方法见名思意,就是发送一个信息,可是要发送到哪里去呢,这是代码:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
调用了sendMessageDelayed
方法:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
继而调用sendMessagAtTime
方法:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w(“Looper”, e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
眼尖的小伙伴就会发现,等等不对,这代码中出了一个叛徒,啊不对,出了一个奇怪的东西。没错,就是刚才流程图中出现的这个MessageQueue
。你看,我没有胡说吧,这个MessageQueue
是实打实存在的,并且被作为参数一起传给了enqueueMessage
方法。其实无论你是如何使用Handler发送消息,结果都会走到enqueueMessage
方法中。
这是方法的调用链:
可以看到无论如何,最后都会走到enqueueMessage
方法中。这个enqueueMessage
方法具体做了什么事呢:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage
一共做了两件事情,一个是给Message
赋值,一个是调用传进来的这个MessageQueue
的enqueueMessage
方法。注意啊,最后这个enqueueMessage
方法是在MessageQueue
中的,已经不再是Handler
的方法了,也就是说,调用走到了这里。事件的流向已经不归Handler
管了。
Handler::enqueueMessage
方法中第一行msg.target = this;
,这个this是什么呢?这个this
在handler
方法中自然是handler
本身了,也就是说这一行代码将handler
自身赋值给了Message
对象的target
字段。我们可以看以下这个target
字段的定义:
//简化后的代码
public final class Message implements Parcelable{
@UnsupportedAppUsage
/package/ Handler target;
}
啊,这样明白了,也就是说每个发出去的Message
都持有把它发出去的Handler
的引用,对不对?
没错事实就是这样,每个发出去的Message
对象内部都会有个把它发出去的Handler
对象的引用,也可以理解Message
这么做的目的,毕竟Handler
把它发射出去了,它不得知道是谁干的,好随后找它报仇么。那么我们继续下一步,msg.setAsynchronous(true)
这一行代码是设置异步消息的,这里暂时先不管它。我们先看queue.enqueueMessage(msg, uptimeMillis)
这行代码。也就是从这行代码,Message
就可以和Handler
说拜拜了您讷。
MessageQueue
Handler
这个mQueue
就是上文我们提到过的MessageQueue
对象,在上面的介绍说也说了,这货就是个骗子,明明起名是Queue
,却是单链表。你可能误会Google工程师了,名字也确实没什么错了,从机制上看确实很像队列。队列是什么特性啊,先进先出对吧。这个先后就是按时间来划分的,时间靠前的就在前面时间靠后的就在后面。而在这个单链表中也确实是这样实现的,按照时间的先后排序。这个就先不多讲了,一会讲如何实现的消息延时发送的时候会讲到这个。
到这里你可能有疑惑了,这个MessageQueue
是什么鬼,从哪里冒出来的。你可能还记得,在上面的sendMessageAtTime
方法中有这么一行:
MessageQueue queue = mQueue;
那么这个mQueue是在哪里被赋值的呢?当然是在构造方法中啦~
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
mCallback = callback;
mAsynchronous = async;
}
不对啊, 你TM骗我,在最开始你继承的Handler可没有这几个参数。哎呀,小伙子别心急,你看这个无参构造方法不也调用的这个方法么。
public Handler() {
this(null, false);
}
在这个有参数的构造方法中呢,可以看到有这么两行:
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;
我们在Handler
中使用的mQueue
就是在这里赋值的。这里的赋值可不简单,它拿的是人家Looper
的MessageQueue
作为自己的MessageQueue
,**而且在上面的代码中有一个很关键的点,就是调用Looper.myLooper()方法中获取这个Looper对象,如果是空的话就要抛出异常。**这一点非常关键,我们先做个记号,一会回过头来会看这一行代码。你就会明白它的作用了。
现在先不研究Looper
,我们继续看我们的MessageQueue
。上面说到,最后发送消息都调用的是MessageQueue
的queue.enqueueMessage(msg, uptimeMillis)
方法。现在我们已经拿到了queue
,进去看看这个方法它做了什么。
// MessageQueue.java
//省略部分代码
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//【1】拿到队列头部
Message p = mMessages;
boolean needWake;
//【2】如果消息不需要延时,或者消息的执行时间比头部消息早,插到队列头部
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//【3】消息插到队列中间
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;