Handler
对于Android系统来说可以是非常重要的了,因为它贯穿了整个Android系统,它随处可见,没有Handler
,Android系统也就不起作用了。
流程介绍
Android系统是以消息驱动的,整个流程可以大致概括为:
发送消息 --> 添加消息到队列 --> 从队列中获取消息 --> 处理消息
上面的流程可以引出几个类分别是,发送消息的Handler
,将消息Message
发送到消息队列MessageQueue
,Looper
从消息队列循环取出消息,然后交给Handler处理。
-
Handler
:消息发送和处理 -
Message
:被发送和处理的消息 -
MessageQueue
:存放消息的消息队列 -
Looper
:循环的从MessageQueue
中取消息给Handler
处理
源码解析
发送消息
发送消息有send
和post
的方式:
// 发送消息
Handler handelr = new Handler();
// 发送方式一
handler.sendMessage(msg); // 或者handler.sendEmptyMessage(what);
// 发送方式二
handler.postXXX(runnable);
handler.sendXXX()
和handler.postXXX()
最终都会调用到sendMessageDelayed()
方法。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 注意sendMessageAtTime方法的第二个参数,当前时间加上延时时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
然后调用了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); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}
又调用了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); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}
然后又调用了MessageQueue
的enqueueMessage()
方法
boolean enqueueMessage(Message msg, long when) { // when = SystemClock.uptimeMillis() + delayMillis
... // 省略无关代码
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 当消息队列为空或者将要入队的消息(msg)的时间(when)在所有消息队列的消息最前面,则把msg插入到队头,最先执行
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
// 消息队列已经不为空了,再插入一个消息时,要先遍历所有的消息,根据时间先后排序,决定将新消息插入在什么位置
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
为空,表示消息队列中还没添加过消息 -
when == 0
:when
表示当前时间SystemClock.uptimeMillis()
加上延迟时间delayMillis
,所以when == 0
这个条件应该是一直返回false -
when < p.when
:新消息的时间when
小于p
消息的时间p.when
以上三个条件只要满足任一条件都会执行到if
代码块中的语句,将新消息添加到p
消息的前面,p
消息为空时,即队头。
当添加新消息时,消息队列中是有消息的,就会执行到else
语句中。先来看看下面代码是什么意思:
prev = p;
p = p.next;
第一行代码中的p
就是在if
代码块中添加的消息,说明指针指向p
消息,第二行代码又将指针指向了p
消息的下一个消息。没错,指针向后移动了一位。为什么要这样呢?
从上面2行代码中可以看出,再次添加消息时,遍历消息都是从添加if
代码块中的消息开始的,因为从if
代码块中添加的消息要么就是第一个消息,要么就是when
小于p
消息的wnen
的,也就是说可确保前面的消息都是时间有序递增的。而系统的时间是一直在增加的,所以enqueueMessage()
方法的第二个参数when
一直大于mMessages
的when
,所以要把指针向后移一位,从后面一个消息开始比对。
上图加深理解(图中所有msg
的when
为了偏于比较和理解,都是用的比较小的数字,而没有使用时间戳):
添加第一个消息时,消息队列中是没有消息的,直接将消息msg1
添加到消息队列的头部。
当添加第二个消息时,消息队列中已经有消息msg1
,此时不满足if
的条件,执行到else
中的代码,因为p = mMessages
,而mMessages
在添加第一个消息时被赋值成了msg1
,代码p = p.next
将指针指向了msg1
的下一个,也就是null
,即p = null
。当p == null
时会跳出循环,会执行到
msg.next = p
prev.next = msg
也就是将msg2
的下一个节点设置成p
,即将msg2
消息插入到p
的前面。如下图:
上面添加的两个消息不是插入到队头就是队尾,再插入一个到中间的消息看看代码是怎么执行的。
在这里插入图片描述
因为mMessage
只在添加msg1
的时候被赋值了,所以p = mMessage = msg1
,接下来执行if
的判断条件if (p == null || when == 0 || when < p.when)
,p = msg
,when = 4
,p.when = 1
,不满足if
条件,执行到else
中的代码。
再来看else
中的for
循环,p = p.next
将指针移到了msg2
的位置,此时p = msg2
,when = 4
,p.when = 5
,满足了for
循环的if
条件when < p.when
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) { // 满足了该条件,跳出循环
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
跳出循环后,执行代码
msg.next = p; // invariant: p == prev.next
prev.next = msg;
将msg3
插入到msg2
前面,至此msg3
的插入流程就走完了。
突然发现,好像除了添加msg1
的时候执行到了if
中的代码,后面都是执行else
的代码。那在什么情况下会再次执行if
中的代码呢?从if
的条件中可以看出,此时p = msg1
不会为空,when
实际上是当前时间的时间戳,也不会等于0,所以只有当when < p.when
的时候才会再次执行到if
中的代码。
那再次发送一个消息msg4
,假设msg.when = 0
(实际上不会为0,这里为了方便假设为0,这个0不等于if
条件中的0),满足条件后执行
msg.next = p;
mMessages = msg;
将msg4
插入到p
的前面,也就是msg1
的前面了,然后又刷新了mMessages
的值了。
总结
- 添加第一个消息时,将消息添加到消息队列的队头
- 再次添加消息时,先判断待添加消息的
when
和p.when
的大小关系,小于p.when
,则把消息添加到p
的前面,否则循环消息队列找出合适的插入位置 - 如果遍历完整个消息队列都没有满足条件的位置,则把新消息插入到队尾
- 消息队列的消息按照时间从先到后排序
取出消息
开头说过整个系统是以消息驱动的,那么Looper
就是其动力了,Looper.loop()
方法不断的从MessageQueue
中取消息。那么Looper.loop()
方法是在哪里调用的呢?由于系统都是由消息驱动的,所以在系统启动的时候就应该有动力驱动了,在SystemServer.main()
中调用了Looper.loop()
,这是在系统层面的驱动,而对于APP应用层面来说,应用入口ActivityThread.main()
中也需要调用Looper.loop()
,毕竟APP
也是需要各种事件响应的嘛。来看看loop()
的代码:
public static void loop() {
final Looper me = myLooper(); // 先获取Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 获取消息队列,获取到了消息队列后才能从消息队列中取出消息
... // 省略无关代码
for (;;) {
Message msg = queue.next(); // 获取消息,无消息时可能会阻塞
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
... // 省略无关代码
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
... // 省略无关代码
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting(); // 开始分发消息
}
try {
msg.target.dispatchMessage(msg); // 分发处理消息
if (observer != null) {
observer.messageDispatched(token, msg); // 消息分发处理完成
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception); // 消息分发异常
}
throw exception;
} finally {
}
... // 省略无关代码
msg.recycleUnchecked(); // 回收消息
}
}
首先要获取到Looper
对象,当Looper
对象为空时抛出异常No Looper; Looper.prepare() wasn't called on this thread.
,我们就知道调用loop()
方法之前需要先调用prepare()
方法,在prepare()
中创建出了Looper
对象。
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
时创建了MessageQueue
,sThreadLocal
保证了一个线程只有一个Looper
,而一个Looper
又只有一个MessageQueue
。
插入话题来看看ThreadLocal
是怎么做到这些的。
回头看看prepare()
方法,首先调用sThreadLocal.get()
获取Looper
,如果不为空说明已经创建过Looper
了,为空就创建一个Looper
保存在sThreadLocal
中,看看sThreadLocal.get()
的实现:
// 获取泛型T,这里是Looper
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
// 获取当前线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 当ThreadLocalMap为空时,设置初始值
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
// 创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
获取到当前线程,然后根据当前线程获取ThreadLocalMap
,ThreadLocalMap
是Thread
的一个变量,表明一个线程只有一个ThreadLocalMap
。接着判断ThreadLocalMap
是否是空,为空则创建一个,否则就获取ThreadLocalMap
中的值。再来看看sThreadLocal.set(T)
方法:
# ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // this就是sThreadLocal,static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
else
createMap(t, value);
}
set()
方法中先是获取了当前线程,然后根据当前线程获取ThreadLocalMap
,当map
为空时就根据当前线程创建一个,否则就将value
设置到map
中。从map.set(this, value)
可以看出,是用当前ThreadLocal
对象sThreadLocal
作为key
,sThreadLocal
是所有线程共享的,一个线程只有一个ThreadLocalMap
,而一个ThreadLocalMap
在key = this
时只能获取到一个value
。
再回头看看Looper.prepare()
方法中的sThreadLocal.get()
方法,从sThreadLocal.set()
方法中我们已经知道了Looper
和线程是一一对应的关系,从当前线程中获取的Looper
不为空时就抛出异常,这样就避免了Looper
再次创建。
话题转回来,获取到了looper
对象后,就从looper
中获取MessageQueue
对象queue
,然后在for(,,)
循环中调用queue.next
取消息,当消息队列中没有消息时,就会阻塞在queue.next()
这里。来看看queue.next()
中做了什么:
Message next() {
... // 省略无关代码
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); // 休眠
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null; // 正常同步消息用不到这个
Message msg = mMessages;
// 当msg,target == null,表示这是同步屏障,msg就是优先执行的消息。
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next; // 将同步屏障的异步消息放在队头,优先执行
} while (msg != null && !msg.isAsynchronous());
}
// 主要看下面的代码
if (msg != null) {
// 代码片段1
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 {
// 代码片段2
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next; // msg将要进行处理,然后会将处理的消息删除,所以消息指针需要向后移一位
}
msg.next = null; // 表示将msg从消息队列中剔除
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1; // 没有消息,继续休眠等待
}
... // 省略无关代码
}
... // 省略无关代码
}
}
next()
里面是一个死循环,enqueueMessage()
和next()
方法中都用了同步锁,避免在发送消息时取消息以及取消息时发送消息,保证了线程安全。主要看synchronized(this)
中的代码,首先获取到msg
,就拿下图中发送的消息来看,此时的msg= mMessages = msg4
,msg
不为空,执行到<u>代码片段1</u>处,然后比较当前时间和msg.when
的大小,如果now < msg.when
,表明还没有到执行该消息的时候,计算出延迟执行时间;否则就执行<u>代码片段2</u>,prevMsg
为空,所以执行mMessages = msg.next
,将指针后移一位,即下图中的msg1
处,然后执行msg.next = null
,将msg4
消息剔除。
这里要注意的是msg.next = null
并不是将msg1
设置为空的意思,而是将msg4
与后面的消息节点断开连接。看下图
获取到消息之后会将消息返回,然后又回到了Looper.loop()
方法的for(;;)
中,返回的消息就是Message msg = queue.next();
中的msg
,然后将msg
分发给handler
处理,再次看上面的loop()
方法,下面贴出关键代码:
try {
msg.target.dispatchMessage(msg); // 分发处理消息
if (observer != null) {
observer.messageDispatched(token, msg); // 消息分发处理完成
}
} catch (Exception exception) {
...
} finally {
...
}
调用到msg.target.dispatchMessage(msg)
方法,那msg.hander
是什么呢?其实它就是Handler
,还记得handler.enqueueMessage()
方法吗?
#Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this; // 在这里设置了target
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
总结
- 必须要先调用
Looper.prepare()
之后再调用Looper.loop()
- 消息队列中没有消息的时候会堵塞在
next()
方法处让CPU休眠,不会ANR - 取到了消息后要判断当前时间和消息执行直接的先后关系,没到执行时间则继续休眠等待,否则就开始处理消息
- 处理消息前,会将该消息从消息队列中剔除
- 最后调用到
handleMessage()
方法处理消息(这条放在这里一起总结了)
处理消息
在Handler
类中发送消息的时候就设置了msg.target
,this
指的就是当前的Handler
对象。再次回到了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);
}
}
msg.callback
是在创建Message
的时候赋值的,而mCallback
是在构造Handler
的时候赋值的
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback; // 给msg.callback赋值
return m;
}
public Handler() {
this(null, false); // 默认callback为空
}
public Handler(@Nullable Callback callback, boolean async) {
... // 省略无关代码
mQueue = mLooper.mQueue;
mCallback = callback; // 给mCallback赋值,默认为空
mAsynchronous = async;
}
因此msg.callback
和mCallback
都为空,接下来执行到handleMessage(msg)
方法
public void handleMessage(@NonNull Message msg) {
}
handleMessage
方法是一个空实现方法,具体的实现在Handler
的回调中
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// 实现具体的逻辑
}
};
扩展
从dispatchMessage()
中处理消息的地方有三处,默认调用的就是handleMessage(msg)
,那什么时候调用其它2种方式呢?
- handleCallback(msg)
当msg.callback
不为空就会执行handleCallback(msg)
,那么就需要先给msg.callback
赋值了。上面说过msg.callback
是在创建Message
的时候赋值的,创建Message
是通过Message.obtain()
来创建的,来看看obtain
方法的重载方法:
public static Message obtain(){}
public static Message obtain(Message orig){}
public static Message obtain(Handler h){}
public static Message obtain(Handler h, Runnable callback){}
public static Message obtain(Handler h, int what){}
public static Message obtain(Handler h, int what, Object obj){}
public static Message obtain(Handler h, int what, int arg1, int arg2){}
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj){}
obtain
有8个重载方法,第四个重载方法可以设置msg.callback
,那在创建Message
的时候可以这样写:
Handler handler = new Handler();
Message message = Message.obtain(handler, new Runnable() {
@Override
public void run() {
}
});
handler.sendMessage(message); // 发送消息
在Message obtain(handler, callback)
中对msg.callback
进行了赋值
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback; // 赋值
return m;
}
然后在分发消息时会执行到handleCallback(msg)
private static void handleCallback(Message message) {
message.callback.run();
}
最终会回调到创建Message
时传入的第二个参数Runnable
的run
方法中。
- mCallback.handleMessage(msg)
msg.callback
的优先级最高,其次是mCallback
,只有在msg.callback
为空时才会执行到mCallback.handleMessage(msg)
,所以在创建Message
的时候不能再传入callback
了。mCallback
是在构造Handler
的时候赋值的,所以要在创建Handler
的时候就要传入mCallback
。来看看Handler
的构造函数:
public Handler(){}
public Handler(@NonNull Looper looper){}
public Handler(@NonNull Looper looper, @Nullable Callback callback){}
public Handler(boolean async){}
public Handler(@Nullable Callback callback){}
public Handler(@Nullable Callback callback, boolean async){} // @hide
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async){}
`
Handler
有7个构造函数,我们可以通过最简单的第五个构造函数来创建Handler
:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
Message message = Message.obtain(handler);
handler.sendMessage(message);
在Handler
的构造函数中对mCallback
进行了赋值,最终会调用到第六个被标上@hide
的构造函数:
public Handler(@Nullable Callback callback, boolean async) {
... // 省略无关代码
mQueue = mLooper.mQueue;
mCallback = callback; // 赋值
mAsynchronous = async;
}
此时,msg.callback
为空,mCallback
不为空,就会调用mCallback.handleMessage(msg)
方法,最后回调到了创建Handler
的参数Handler.Callback
的handleMessage(msg)
中。
网友评论