本系列相关阅读
前篇回顾
-
Android的消息机制主要是指Handler运行机制
由于Android开发规范明确表示只能在主线程更新UI,因此当子线程完成工作,我们需要使用Handler切换到主线程执行UI更新操作。 -
MessageQueue 消息队列
用来缓存一系列待处理的消息,它的内部存储结构并不是真正的队列,而是采用单链表结构来存储消息列表。 -
Looper 循环
MessageQueue不能处理消息,这个工作由Looper来完成。它是以无限循环的形式去检查消息队列中是否有新消息,如果有的话就处理消息,如果没有就一直等待。 -
ThreadLocal
一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。Handler中主要用它来存储当前线程的Looper对象。
上一篇已经介绍了Handler的简单使用,本文简单描述一下Handler的工作过程。
Handler创建完成之后,内部的MessageQueue和Looper就会和Handler一起协同工作。Handler拥有一系列post/send方法,通过源码我们可以发现,post方法最终还是通过send方法来完成的,我们接下来从源码角度分析Handler的工作流程:
1. Handler把消息放入消息队列
Handler开始工作的第一步就是调用send系列方法,最终调用的是sendMessageAtTime方法,查看源码:
public boolean sendMessageAtTime(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这段代码比较简单,sendMessageAtTime方法内部会调用MessageQueue的enqueueMessage方法,将消息放入到消息队列中去。有一个小细节需要注意,消息放入到消息队列之前,会先将这个消息与发送它的Handler进行绑定,也就是使用一个变量 msg.target来存储当前Handler的引用,在接下来的消息处理中会用到它。
2. Looper从消息队列中取出消息并交给Handler处理
通过上一篇文章我们知道,Looper的主要工作就是不停地检查消息队列中是否有新消息:如果消息队列中有新消息,取出消息并交给Handler处理;如果没有消息就一直阻塞在那里。我们很容易就可以猜到这些任务都是在Looper.loop()方法中完成的,为了验证我们的猜想,接下来我们看看loop方法:
public static void loop() {
final Looper me = myLooper();
if (me == null) { //判断在这之前是否执行过Looper.prepare()
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
……
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// 没有消息说明消息队列正在退出
return;
}
try {
msg.target.dispatchMessage(msg); //把消息交给Handler处理
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
上面是loop方法的核心代码,主要步骤如下
- 首先通过ThreadLocal获取当前线程的Looper:如果Looper为null,说明当前线程还没有执行过Looper.prepare()方法,抛出异常提示用户当前线程没有Looper;Looper不为空就继续向下执行。
- 使用无限for循环检查消息队列中是否有新消息:如果没有新消息则表明消息队列正在退出,整个消息循环也就结束了;如果有新消息,调用Handler的dispatchMessage方法,把消息传递给Handler去处理。需要注意的是,处理消息的Handler必须是把消息放入消息队列中的那个Handler,由于在enqueueMessage方法中我们使用msg.target存储了当前Handler的引用,所以直接使用msg.target来处理消息即可。
3. Handler对消息进行处理
Handler的dispatchMessage方法主要负责处理Looper发送过来的消息:
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //消息是用post系列方法加入到消息队列中的
handleCallback(msg);
} else {//消息是用send系列方法加入到消息队列中的
if (mCallback != null) { //用接口作为参数创建Handler对象
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //继承Handler类的时候,通常都要重写这个方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
dispatchMessage方法内部逻辑在代码中已经注释,具体流程见下图。
消息处理流程图
参考
《Android开发艺术探索》
网友评论