Handler在Android中负责调度消息并将来某个时段处理消息。Android有大量的消息驱动方式来进行交互,比如四大组件的的启动过程的交互,都离不开消息机制。
消息机制涉及MessageQueue/Message/Looper/Handler这4个类。
Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
MessageQueue:消息队列的主要功能向消息池投递消息和取走消息池的消息;
Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。
梳理过程从handler构造函数开始,然后产生一条消息通过sendMessage向下分发。之后又是如何被调度,最后被怎样处理。
public Handler(@Nullable 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;
}
这里是通过Looper中的myLooper方法来获得Looper实例的,如果Looper为null的话就会抛异常,抛出的异常内容翻译过来就是无法在未调用 Looper.prepare()的线程内创建handler,new handler的时候没有报错那么就一定之前就调用了 Looper.Prepare(),在哪里创建的呢?
答案在 ActivityThread.main() 中。
ActivityThread 的 main()方法就是整个APP的入口,也就是我们通常所讲的主线程, UI线程。但他实际上并不是一个线程,ActivityThread 并没有继承 Thread 类,我们可以把他理解为主线程的管理者,负责管理主线程的执行,调度等操作,看下main方法的源码。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
...
}
两个关键方法 Looper.prepareMainLooper()和Looper.loop(),loop方法后面再分析,继续看prepareMainLooper的方法内部如下:
// 1
public static void prepareMainLooper() {
// 2
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 2
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 3
sThreadLocal.set(new Looper(quitAllowed));
}
// 3
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
到1⃣️这里就解决了,为什么我们在主线程中使用Handler之前没有调用Looper.prepare方法的问题了,就是在这里调用了prepare 方法。
2⃣️prepare方法内部把 Looper 放到ThreadLocal中与当前线程绑定,保证一个线程只有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
3⃣️在Looper的构造函数中始化MessageQueue
所以初始化Handler之前Looper 和 MessageQueue就已经初始化了,并把Looper放到ThreadLocal中。ThreadLocal有个特点是你set进去的值是以当前所处的线程为key,value就是你set的值。源码如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Handler的sendMessage方法都做了什么
先看一下sendMessage的流程图:
简单过一遍,sendXXX 这些方式最终还是会调用到 enqueueMessage 这个方法上来的逻辑最后调用queue.enqueueMessage 添加消息到消息队列MessageQueue中,下面就是消息的入队逻辑
boolean enqueueMessage(Message msg, long when) {
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.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) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
消息入队顺序是按照 Message 触发时间 long when入队的,有新消息加入时,会循环遍历当前消息队列,对比消息触发时间,直到找到当前消息合适的插入位置,以此来保证所有消息的触发时间顺序。即 MessageQueue 添加消息到消息队列中。
所以消息的存储与管理MessageQueue 来负责
谁负责把消息分发出去
Handler把Message交给MessageQueue并存储起来 ,那么谁来负责把我这里的消息分发出去并且告诉主线程的人(Handler)呢?答案就是 Looper 了!
之前分析的ActivityThread.main()中的Looper.loop还没有讲到我们接着跟进去看一下源码
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
// 循环调用 MessageQueue 的 next 方法获取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
loop()不断从MessageQueue中取消息,把消息交给target(handler)的dispatchMessage方法处理。下面是dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到dispatchMessage最后调用了handleMessage方法,在源码里面是个空实现。需要我们在创建handler的时候复写,根据不同的message处理不同的业务逻辑。
整个Handler的消息分发调度流程就讲完了,为了增强记忆,我再把整个流程拼成一块如下图: