前言
Android提供了Handler来满足线程间的通信,开发中不管直接还是间接基本离不开Handler的使用,通常Handler被我们用来做子线程更新UI线程的工具,可以说只要有子线程与主线程通信的地方就会有Handler。
工欲善其事必先利其器,熟悉Handler机制可以帮助我们更好的处理工作中遇到的问题。
1. 使用示例
private Handler mHandler;
public void test() {new Thread(){@Overridepublic void run() {Looper.prepare();initHandler();Looper.loop();}}.start();
}
private void initHandler() {mHandler = new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {// 处理消息}};
}// 通过Handler发送消息
mHandler.sendMessage(message);
mHandler.post(runnable);
...
2. Java源码分析
主要涉及到以下几个类:
- Message (消息实体)
- Handler (处理Message)
- MessageQueue (维护Message的队列,插入和取出Message)
- looper (循环从MessageQueue中取Message)
Handler机制的核心是发送与处理Message,那么我们先过一遍Message类。
2.1 Message实体类
public final class Message implements Parcelable {...// 处理Message的时间点public long when;// 持有处理Message的Handler对象/*package*/ Handler target;// 用来记录Message链表的下一个节点/*package*/ Message next;// Message复用池(也是链表格式)private static Message sPool;// Message复用池中当前Message个数private static int sPoolSize = 0;// Message复用池最大Message个数private static final int MAX_POOL_SIZE = 50;...// 从Message复用池中获取Message,如果没有缓存才创建新的Messagepublic static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // 清除in-use标志sPoolSize--;return m;}}return new Message();}void recycleUnchecked() {// 将消息标记为in-use,清空其他信息,插入Message复用池flags = FLAG_IN_USE;what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = UID_NONE;workSourceUid = UID_NONE;when = 0;target = null;callback = null;data = null;synchronized (sPoolSync) {if (sPoolSize < MAX_POOL_SIZE) {next = sPool;sPool = this;sPoolSize++;}}}// 是否异步消息public boolean isAsynchronous() {return (flags & FLAG_ASYNCHRONOUS) != 0;}// 设置为同步或者异步消息public void setAsynchronous(boolean async) {if (async) {flags |= FLAG_ASYNCHRONOUS;} else {flags &= ~FLAG_ASYNCHRONOUS;}}...}
Message是一个封装消息的实体类,主要对简单的消息做一层包装,方便后续处理,里面几个比较重要的字段和方法如上,其中when是待处理消息的时间点,target是Handler对象的引用,sPool是消息复用池,因为Android交互是基于消息机制,如果不进行复用,频繁的处理消息就会不断的创建销毁Message对象,导致内存抖动,所以创建Message时,Google推荐使用Message.obtain()来获取Message对象。recycleUnchecked()是将当前Message清空并插入复用队列。
2.2 发送Message流程
主要是通过Handler将Message按照时间When字段插入MessageQueue中。
2.2.1 Handler类
我们先来看sendMessage(message)和post(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");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);
}
可以看到,不管是sendMessage还是post都调用了sendMessageDelayed方法,而sendMessageDelayed里面调用了sendMessageAtTime并传入SystemClock.uptimeMillis() + delayMillis(当前时间+延迟时间)参数,也就是消息真正待处理的时间点,sendMessageAtTime又调用了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);
}
在Handler的enqueueMessage方法中,调用了MessageQueue的enqueueMessage方法;注意这里有一个重点,Message的target指向了当前Handler对象,也就是说每个Message消息都会持有Handler对象的引用,这也就是为什么非静态内部类Handler可能会出现内存泄漏的原因。
2.2.2 MessageQueue类
将Message插入MessageQueue。
boolean enqueueMessage(Message msg, long when) {...// 同步锁,多线程同步的核心synchronized (this) {...// when = 当前时间 + 延迟时间msg.when = when;// 获取MessageQueue中保存的Message队列头Message p = mMessages;boolean needWake;// 如果Message队列为空或者新Message的when小于队列头// 将新Message插入到队列头部if (p == null || when == 0 || when < p.when) {msg.next = p;mMessages = msg;// 如果阻塞则唤醒事件needWake = mBlocked;} else {// 插入到队列中间。通常不必唤醒事件队列,除非// 队列的头部有屏障,并且消息是队列中最早的异步消息。needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;// 根据当前Message的when循环查找合适的位置插入,保证队列中// 的Message根据时间顺序排列。for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; prev.next = msg;}if (needWake) {nativeWake(mPtr);}}return true;
}
MessageQueue中持有一个链表形式的Message队列,enqueueMessage方法主要就是将待发送的Message根据时间顺序插入到消息队列中,并判断是否需要唤醒处理消息队列的线程,如果需要,通过调用native方法nativeWake()将该线程唤醒。
2.3 取出Message流程
主要是通过Looper循环从MessageQueue中拿出时间When字段与当前时间匹配的Message。
2.3.1 Looper类
Looper.prepare()主要是初始化Looper,并通过ThreadLocal能力保证此线程只有一个Looper,这里我就不细讲了。
我们看另外比较重要的Looper.loop()方法。
public static void loop() {...for (;;) {Message msg = queue.next(); // 可能阻塞if (msg == null) {// 没有消息表明消息队列调用了退出return;}...msg.target.dispatchMessage(msg);...msg.recycleUnchecked();}
}
loop方法的主要作用是循环从MessageQueue中获取Message,并通过Message.Target(也就是Handler)调用dispatchMessage。
我们看看MessageQueue的next方法。
2.3.2 MessageQueue类
Message next() {...int nextPollTimeoutMillis = 0;for (;;) {...nativePollOnce(ptr, nextPollTimeoutMillis);// 同步锁,保证多线程之间Message数据同步synchronized (this) {// 尝试检索下一条Message,找到就返回final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// 同步屏障,查找下一条异步消息do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 还未到消息的执行时间,计算时间差nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 当前Message执行时间已到,返回此MessagemBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;msg.markInUse();return msg;}} else {// 没有更多MessagenextPollTimeoutMillis = -1;}...}...nextPollTimeoutMillis = 0;}
}
外部循环去检索Message,内部do while循环增加同步屏障检索异步Message,如果有异步Message则先处理异步,拿到Message之后判断时间when是否晚于当前时间,晚于当前时间的话计算时间差并保存到nextPollTimeMills,待下次循环开始然后调用native方法nativePollOnce()来进行阻塞,nativePollOnce()方法在native层使用到了Linux的epoll机制,并且native层也有一套Handler机制,这里先不做细致讲解,免得增加复杂度,总之内部在没有到处理消息的时间时会阻塞线程,释放CPU,直到拿到当前时间到达目标时间When的Message。
2.3.3 Handler类
接下来我们进入dispatchMessage看看做了啥。
public void dispatchMessage(@NonNull 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();}
可以看到,dispatchMessage中判断msg.callback不为空则调用callback的run方法,也就是我们通过Handler.post(runnable)等传入的Runnable的run方法,否则调用handleMessage。
3. 整体流程

4. 小结
- Handler类主要处理收发消息,通过调用MessageQueue的enqueueMessage()方法将Handler发送的Message插入MessageQueue,并且内部有同步锁,保证多线程同步。
- Looper主要通过loop()方法循从MessageQueue中取出Message,从MessageQueue中取出消息内部也有同步锁,保证多线程同步。
- Handler消息机制,在Java层的MessageQueue中,调用了native方法实现线程的休眠和唤醒。native方法nativePollOnce()实现了消息循环的休眠,而nativeWake()方法则实现了消息循环的唤醒。
















