1.简介
Handler 是一套 Android 消息传递机制,主要用于线程间通信。
为什要有Handler机制?
解决在子线程更新UI的问题
由于在Android机制中,为了保证UI操作是线程安全的,规定只允许在原始线程更新UI,但在实际开发中存在多个线程并发操作UI组件的情况,会导致线程不安全,所以采用Handler机制,当子线程需要操作UI组件时,通过Handler通知主线程,从而在主线程中更新UI。
2.介绍
Hnadler机制重要组成部分
- Meesgae:需要传递的消息,可以传递数据及对象;
- MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能是向消息池投递消息(enqueueMessage)和和取走消息池的消息(next)
- Handler:消息的真正处理者,主要功能是向消息队列发送消息(sendMessage)和处理对应的消息(handleMessage)
- Looper:消息轮训器,重要功能是,不断的从消息队列中取出消息交与Handler处理
Handler流程介绍
发送消息 --> 添加消息到队列 --> 从队列中获取消息 --> 处理消息
由Handler发送Messgae开始,将Message发送到MessageQueue中,由Looper不断的轮训去除MessageQueue中的Message,交给Handler处理 ,大致流程如下

源码解读
发送消息的方式主要有sendXXX()和postXXX()两种,
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
handler.sendXXX()和handler.postXXX()最终都会调用到sendMessageAtTime()方法。
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);
}
然后调用 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);
}
在这里将Hanlder本身赋值给Messgae的target,让后调用MessageQueue的enqueueMessage(),将Message加入到MessageQueue的队列当中,接下来就到MessageQueue的enqueueMessage()方法当中去看。
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 当消息队列为空或者将要入队的消息(msg)的时间(when)在所有消息队列的消息最前面,则把msg插入到队头,最先执行
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 消息队列已经不为空了,再插入一个消息时,要先遍历所有的消息,根据时间先后排序,决定将新消息插入在什么位置
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
// 当p为空(遍历到最后一个了),或者新消息的时间在p的时间之前,就不用再遍历了,因为可以确定新消息要插在消息p前面了
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// break跳出for循环后执行到这里,将msg插入到p前面,prev后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
......
}
return true;
}
看Message类的实现可以看出它是个单链表,消息队列中的消息都是按照时间先后顺序链接起来的。
接下来就详细的分析一下上述的代码,首先我们看一下这个if (p == null || when == 0 || when < p.when)判断:
- p == null:我们可以从上面看到p 就是 mMessages;而mMessages 是在我们if判断里面赋值的,所以我们第一次进入时,p也就是mMessages为空,表示MessageQueue中没有添加过消息。
- when == 0 :when是我们从Hanlder当中传过来的值,表示当前时间
SystemClock.uptimeMillis()加上延迟时间delayMillis - when < p.when:判断新Messgae.when是否小于p.when
以上三个判断条件满足一个就会进入if代码块中,将新消息放在p消息的前面,当满足p==null时,即为队头,就是以下情况:
当满足p!=null且when == 0 || when < p.when为true时,就是以下这种情况:
当以上三个条件不满足时,就会执行else的代码块,先来看一下下面这段代码的意思:
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
......
}
// break跳出for循环后执行到这里,将msg插入到p前面,prev后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
第一行代码中的P消息上在if代码块中添加的消息,说明指针指向P消息,第二行p消息指向下一个消息,指针小猴移动了一位。 从第二行代码可以看出,再次添加消息时,遍历消息都是从添加if代码块中的消息开始的,因为从if代码块中添加的消息要么就是第一个消息,要么就是when小于p消息的wnen的,也就是说可确保前面的消息都是时间有序递增的。而系统的时间是一直在增加的,所以enqueueMessage()方法的第二个参数when一直大于mMessages的when,所以要把指针向后移一位,从后面一个消息开始比对。
以上两种都是在队头队尾添加的,再看一个从插入到队列中间的情况:
总结
- 添加第一个消息时,将消息添加到消息队列的队头
- 再次添加消息时,先判断待添加消息的
when和p.when的大小关系,小于p.when,则把消息添加到p的前面,否则循环消息队列找出合适的插入位置 - 如果遍历完整个消息队列都没有满足条件的位置,则把新消息插入到队尾
- 消息队列的消息按照时间从先到后排序
取出消息
文章开始已经说了Looper是消息轮询器,Looper.loop**不断的从MessageQueue中取出Message,那么Looper.loop()是在哪里调用的呢?由于Android是由消息驱动的,一定是在app的入口ActivityThread的main()方法中调用了,下面看下Looper.prepare()的创建和Looper.loop()轮训.
Looper.prepare()
对于app的入口ActivityThread调用的是Looper.prepareMainLooper()实际上也是调用了Looper.prepare()。下面一步步来看一下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在Looper.prepareMainLooper()中调用了Looper.prepare(),在Looper.prepare()中首先判断当前ThreadLocal中是否已经存在Lopper对象,如果存在则抛出异常,一个线程对应一个一个Looer对象。如果不存在Looper对象则 new一个出来,在Looper的构造方法中创建了MessageQueue。
ThreadLocal保证了一个线程只有一个Looper对象,一个Looper对象只有一个MessageQueue。看一下ThreadLocal是如何做到的。回头看下sThreadLocal.get()方法。
public T get() {//这里的泛型指的是Looper
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//获取当前线程中的Looper
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果map==null||looper==null执行以下方法
return setInitialValue();
}
private T setInitialValue() {
//初始化value(Looper)当然为空,在后面ThreadLocal.set()方法会赋上具体的值
T value = initialValue();
//获取当前线程ThradLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//添加进map
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
首先获取当前线程,根据当前线程获取到ThreadLocalMap对象,ThreadLocalMap是ThreadLocal中的一个内部类,表明一个Thread只存在一个ThreadLocalMap,然后ThreadLocal.get()方法中判断ThreadLocalMap是否为空、ThreadLocalMap中是否存在Looper对象,如果存在则返回该Looper对象,如果ThreadLocalMap为空或者ThreadLocalMap中不存在Looper对象,则创建ThreadLocalMap,赋值一个初始的空对象,等待ThreadLocal.set()方法赋具体的值。
Looper.loop()
public static void loop() {
//获取到Looper对象
final Looper me = myLooper();
......
me.mInLoop = true;
//根据Looper对象获取MessageQueue
final MessageQueue queue = me.mQueue;
......
for (;;) {
// 从MessageQueue中取出消息,无消息时可能会阻塞
Message msg = queue.next();
......
try {
//交与Handler处理消息
msg.target.dispatchMessage(msg);
......
} catch (Exception exception) {
......
throw exception;
} finally {
......
}
......
//回收消息
msg.recycleUnchecked();
}
}
首先获取Looper对象,根据Looper对象获取到MessageQueue队列,不断的调用next()方法来获取Message,交与Handler去处理,最后回收该消息。
下面看一下MessageQueue.next()做了些什么
// MessageQueue.java 中的 next 方法源码
Message next() {
// 判断 native 层的 MessageQueue 对象有没有正常创建
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 消息执行需要等待的时间
int nextPollTimeoutMillis = 0;
for (;;) {
// 执行 native 层的消息延迟等待,调 next 方法第一次不会进来
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 获取当前系统的时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
if (now < msg.when) {
// 需要延迟, 计算延迟时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 不需要延迟获取已经过了时间,立马返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
// 标记为已在使用状态
msg.markInUse();
return msg;
}
} else {
// 如果队列里面没有消息,等待时间是 -1
nextPollTimeoutMillis = -1;
}
// 有没有空闲的 IdleHandler 需要执行,一般我们没关注这个功能
// 后面内容有专门解释,这里目前分析是 == 0 ,跳出
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
...
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
通过源码分析我们发现消息的处理过程,是通过当前消息的执行时间与当前系统时间做比较,如果小于等于当前系统时间则立即返回执行该消息,如果大于当前系统时间则调用 nativePollOnce 方法去延迟等待被唤醒,当消息队列里面为空时则设置等待的时间为 -1。
总结
- 调用
Looper.loop()之前必须调用Looper.prepare() - 消息队列中没有消息的时候会堵塞在
next()方法处让CPU休眠,不会ANR - 取到了消息后要判断当前时间和消息执行直接的先后关系,没到执行时间则继续休眠等待,否则就直接处理消息
- 处理消息前,会将该消息从消息队列中去除
处理消息
在Looper.loop()中调用 msg.target.dispatchMessage(msg)处理消息, msg.target为Hanlder,在enqueueMessage()中赋值的。
下面看下dispatchMessage()。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback是在Message.obtain()方法中赋值的
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
如果msg.callback不为空的话,就会执行handleCallback(msg)方法,就是去处理这个Runnable对象
private static void handleCallback(Message message) {
message.callback.run();
}
如果msg.callback为空的话,就会判断Handler中的回调是否为空,这个callback对象是在Handler创建时选择参数,如果不为空的情况下,就会执行callback中的handleMessage()方法
public Handler(@Nullable Callback callback, boolean async) {
.......
mCallback = callback;
}
最后执行到我们常用的handleMessage(msg),这个方法就是一个空的模版方法,交由我们去实现具体的逻辑。
public void handleMessage(@NonNull Message msg) {
}
至此Handler整个流程执行完毕。
拓展
Message创建
Message创建的方法主要有两种
- 通过构造方法new对象
- 通过
obtain()从池子里去Message对象
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();
}
通过obtain()从池子里去Message对象,这里才用了享元设计模式,从池子里取出Message对象复用,避免了不必要的对象创建,减少了内存开销,题高了资源的利用率.因此个人认为使用这种方法创建对象比较好。