Handler 是一个消息分发对象。handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息。
以下是一段常见的Handler
在进行多线程通信时的代码(使用Kotlin
语言实现),在主线程中开启子线程,在子线程中通过主线程的Handler
向主线程发送Message
,最后在主线程中接收消息并处理:
/**
* 运行后打印日志:
* -------->>Starting sub thread...
* -------->>Sending message at thread `Thread-2`...
* -------->>Message received at thread `main`...
*/
class MainActivity : AppCompatActivity() {
private val customHandler: Handler = CustomHandler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println("-------->>Starting sub thread...")
CustomThread().start()
}
inner class CustomThread : Thread() {
override fun run() {
println("-------->>Sending message at thread `${Thread.currentThread().name}`...")
val msg = Message.obtain()
customHandler.sendMessage(msg)
super.run()
}
}
class CustomHandler : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
println("-------->>Message received at thread `${Thread.currentThread().name}`...")
}
}
}
下面对上述代码进行源码分析,首先看Handler
的创建过程。以下列出Handler
的所有构造函数及其相互之间的调用关系:
public Handler() { this(null, false); }
public Handler(Callback callback) { this(callback, false); }
public Handler(Looper looper) { this(looper, null, false); }
public Handler(Looper looper, Callback callback) { this(looper, callback, false); }
public Handler(boolean async) { this(null, async); }
public Handler(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;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以发现以下几点:第一,所有其他构造方法最终都会调用到最后两个构造方法;第二,在构造方法中做的事情都是校验参数和为私有变量赋值;第三,async
这个参数在不手动传值时,永远被置为false
。
再来看其余两个参数Callback
和Looper
。
Callback
是一个接口,里面唯一的抽象方法就是我们熟悉的handleMessage()
方法,用于在接收到消息时回调,代码如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
想了解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
的,需要通过依次调用Looper.prepare()
和Looper.loop()
来创建。那么问题来了,这两个方法里面都做了什么呢?为什么这两个方法中都没有传入线程对象,就可以绑定到线程呢?Looper
的作用到底是什么呢?带着这些问题,我们来看一下Loope
类的源码,先来看与Looper#prepare()
方法相关的源码:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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));
}
从上述代码中可以总结出prepare()
方法的执行流程:创建一个新的Looper
对象,初始化其两个成员变量mQueue
和mThread
,然后将这个Looper
存储到一个ThreadLocal
对象中。从以上源码可以总结出以下几点:第一,ThreadLocal
是线程私有的,因此这个Looper
只被这一个线程享有;第二,一个线程中最多只能有一个Looper
,因为如果调用多次prepare()
方法,会抛出异常Only one Looper may be created per thread
;第三,在哪个线程中调用Looper.prepare()
,创建出来的Looper
就会绑定到哪个线程,因为mThread
变量的初始化方式是Thread.currentThread()
,即当前所在线程。
综合以上信息,可以得出结论:一个线程可能绑定了0个或1个Looper,但一个Looper一定绑定了一个线程。
再来看Looper#loop()
方法的源码,由于此方法中代码较多,因此这里只贴出核心代码:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
}
msg.recycleUnchecked();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到,在loop()
方法中首先通过myLooper()
方法,从ThreadLocal
中获取当前线程的Looper
对象,然后获取该Looper
中的MessageQueue
消息队列;随后开启了一个无限循环,从消息队列中无限循环的取出Message
对象,并调用其target
对象的dispatchMessage()
方法。这里最终操作的对象是Message
,因此我们先来看一下Message
类的源码:
public final class Message implements Parcelable {
static final int FLAG_IN_USE = 1 << 0;
Handler target;
Message next;
Runnable callback;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
// ... reset data
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
从Message
的核心源码中可以总结出以下几点:第一,Message
是一个链表结构,因为其next
属性指向了一个Message
类型的对象;第二,Message
类中维护了一个静态的sPool
对象,即一个对象池,也是一个链表结构,用于方便的存取Message
对象,提升性能;第三,官方推荐使用的Message.obtain()
方法,其实就是从sPool
对象池中取出头节点使用;当调用recycleUnchecked()
方法回收对象时,会将重置后的Message
对象添加到sPool
对象池的头部;第四,Message
类中的target
属性是一个Handler
类型的对象,即要处理此消息的对象。
再来看MessageQueue#next()
方法相关的源码:
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1; // No more messages.
}
// ...
return null;
}
}
}
从源码中可以看出,MessageQueue#next()
方法的原理是:取出队列中的第一条消息,并判断这条消息的预计执行时间是否晚于当前时间,如果晚于当前时间则说明现在还不是执行这条消息的时机,此时会调用Binder.flushPendingCommands()
和nativePollOnce(ptr, nextPollTimeoutMillis)
(这两个方法都是被native
关键字声明的方法,底层调用的是操作系统的原生方法),将这段时间差值过渡过去,直到队列中的第一条消息达到了执行时机,此时才返回这条消息。这就是为什么在调用Handler#postDelayed()
方法时,发出的消息会在延迟一段时间后才执行。
到此我们不免会有一个新的疑问:假如我们通过Handler
依次发出三条消息,其延时分别是2000ms
、10ms
和3000ms
,而MessageQueue
每次调用next()
方法都是返回第一条消息,其内部是怎样处理这些消息的存放顺序的呢?接下来我们来看一下MessageQueue#enqueueMessage()
方法的源码:
Message mMessages;
boolean enqueueMessage(Message msg, long when) {
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) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
} else {
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
可以看到,MessageQueue
中存储的Message
是根据消息执行的时间先后来排序的,在队列为空、消息没有延时、消息执行时间早于当前时间这三种情况下,消息会被直接添加到队列的头部;否则,则从头开始遍历队列中的消息,将消息插入到队列中,以保证当前消息前面的消息都早于它被执行,当前消息后面的消息都晚于它被执行。
到此为止,Looper
、MessageQueue
、Message
相关的代码就都分析完了,接下来看一下Handler
在发送消息时都发生了什么。Handler
中用来发送消息的方法有很多,下面列出部分常见方法的签名:
public final boolean post(Runnable r) {}
public final boolean postAtTime(Runnable r, long uptimeMillis) {}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {}
public final boolean postDelayed(Runnable r, long delayMillis) {}
public final boolean postDelayed(Runnable r, Object token, long delayMillis) {}
public final boolean sendMessage(Message msg) {}
public final boolean sendEmptyMessage(int what) {}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {}
上述方法中,如果参数中传了Runnable
,则会将这个Runnable
赋值给Message#callback
;如果是sendEmptyMessageXXX()
,则会先通过Message.obtain()
方法从消息池中取出一个消息,然后设置其what
属性。但最终,上述方法都会调用下面这个方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,enqueueMessage()
方法主要做了以下两件事:
- 将
Message
对象的target
属性设置为自己,这样就形成了一个闭环:本Handler
在子线程中发送消息,由于消息关联的target
属性也是本Handler
,因此本Handler
也是消息最终的处理者。消息虽然存储在子线程的Looper
关联的MessageQueue
中,但它持有本Handler
的引用,因此当消息被执行时,实际上也是已经回到了当前的线程中。 - 将
Message
入队列到本Handler
关联的MessageQueue
中。需要强调的是,这里的uptimeMillis
是通过SystemClock.uptimeMillis() + delayMillis
方法计算出来的,即用当前系统启动时间加上延时时间,得到的是一个具体的时间戳。
还记得Looper#loop()
方法中,获取到消息后执行的msg.target.dispatchMessage(msg)
吗,现在已经知道了msg.target
实际上就是发出这个消息的Handler
,那么我们再来看看Handler#dispatchMessage()
方法中做了什么吧:
final Callback mCallback;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void handleMessage(Message msg) {
}
可以看到,这里进行了多重条件判断:
- 如果
Message
中设置了callback
(Runnable
),则调用其run()
方法,结束; - 如果
Message
中没有设置callback
,则判断本Handler
中有没有设置mCallback
(Callback
),如果有则调用其handleMessage()
方法,判断其返回值,如果返回true
则结束; - 如果上述几个条件都不符合,则会调用本
Handler
中的handleMessage()
方法,这个方法可以被重写。
综上可得,Handler
中事件处理的优先级是:Message#Runnable#run()
> Handler#Callback#handleMessage()
> Handler#handleMessage()
。
最后总结一下Handler机制
- 创建
Handler
的时候,通过Looper.myLooper()
获取当前线程下的Looper
对象(如果当前线程下没有Looper
会报错,因此子线程中需要先通过Looper.prepare()
、Looper.loop()
创建并启动一个Looper
对象),然后将Looper
对象和Looper#MessageQueue
对象赋值给当前Handler
; Handler
对象通过postXXX()
、sendXXX()
方法发送消息,最终都会调用到enqueueMessage()
方法,这个方法做了两件事:将当前Handler
赋值给Message#target
对象、将Message
对象入队列到MessageQueue
中;MessageQueue
中按照Message
被执行的时间早晚进行排序,新入队列的Message
会被插入到队列中合适的位置,以保证时间的先后顺序;- 当试图从
MessageQueue
中取出一条消息时,会判断队列中的第一条消息是否达到了执行时间,如果没有达到,则会调用操作系统层的native
方法进行时间过渡,直到达到消息的执行时间为止; - 当调用了线程中
Looper
对象的loop()
方法时,会无限循环的从MessageQueue
中取出消息,取出消息后即会调用msg.target.dispatchMessage()
方法,即将消息派发给发送这条消息的Handler
,由这个Handler
处理消息(此时已经回到了Handler
被创建时的线程); Handler
会按照Message#Runnable#run() -> Handler#Callback#handleMessage() -> Handler#handleMessage()
的优先级顺序执行Message
。
补充一些上面没有提到的细节
1. 怎样在子线程中使用Handler
在子线程中使用Handler
的方式有两种。
第一种方式是上面说到的,由于Handler
依赖Looper
实现,而子线程中默认又没有Looper
,因此我们需要先创建Looper
,然后再创建并使用Handler
。具体方法如下(以下方法使用Kotlin
语言实现):
inner class CustomThread : Thread() {
override fun run() {
super.run()
// 创建 Looper 并与当前线程(子线程)绑定
Looper.prepare()
// 创建 Handler 对象,此时因为已经初始化了 Looper,因此不会报错
val handler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
}
}
// ... 操作 Handler 对象
// 别忘了调用 Looper.loop() 方法,启动 Looper
Looper.loop()
}
}
第二种方法是使用HandlerThread
代替Thread
创建子线程。HandlerThread
是Thread
的子类,其中默认实现并持有了一个Handler
,源码如下:
public class HandlerThread extends Thread {
Looper mLooper;
private @Nullable Handler mHandler;
// ...
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
}
2. 主线程中不需要初始化Looper,为什么可以使用Handler
答案是:Android
系统已经在主线程中帮我们初始化好了Looper
。看以下一段ActivityThread
类中的代码:
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
// ...
Looper.loop();
}
}
可见,在应用刚运行起来的时候,主线程中的Looper
就已经被创建了。接下来看一下Looper.prepareMainLooper()
中的代码:
private static Looper sMainLooper; // guarded by Looper.class
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到,Looper.prepareMainLooper()
方法最终就是调用了Looper.prepare()
方法。