前言
Handler
可以说小伙伴们用的非常多了,可以说Handler
是支撑整个Android系统运行的基础,本质上Android系统都是由事件驱动的。而处理事件的核心就在于Handler
。接下来我们就从简单的使用,到源码分析让你彻彻底底明白Handler
的本质。不会再让你发出为什么Looper.loop
不会堵塞主线程,Handler是如何切换线程等这类疑惑。
作者:Mlx
链接:https://juejin.im/post/6866015512192876557
简单使用
一般是在主线程中实现一个Handler,然后在子线程中使用它。
class HandlerActivity: AppCompatActivity() {
private val mHandler = MyHandler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在子线程中通过自定义的 Handler 发消息
thread {
mHandler.sendEmptyMessageDelayed(1, 1000)
}
}
// 自定义一个 Handler
class MyHandler: Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "主线程:handleMessage: ${msg.what}")
}
}
}
或者有时候需要在子线程中创建运行在主线程中的Handler
class HandlerActivity: AppCompatActivity() {
private var mHandler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
thread {
//获得main looper 运行在主线程
mHandler = MyHandler(Looper.getMainLooper())
mHandler!!.sendEmptyMessageDelayed(1, 1000)
}
}
// 自定义一个 Handler
class MyHandler(): Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "子线程:handleMessage: ${msg.what}")
}
}
}
这就是小伙伴们一般常用的两个用法。大家注意到了在第二个用法中出现了一个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
中去。而这个MessageQueue
就是被包裹在了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
,**而且在上面的代码中有