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()方法。