大致流程
将当前线程转成Looper线程;Handler发送消息,插到MessageQueue(消息队列);looper进入循环,如果在消息队列中拿到Message就交给Handler,如果消息队列中没有消息则阻塞。
线程的分类及通信
我们都比较明白线程可分为主线程和工作线程,还有线程之间的通信分为主线程与工作线程、工作线程与工作线程(比较少听到)。
相关的对象
在上一篇基本都有提到,在这继续说一下
- Message:线程间通信的信息,是消息的载体;
- MessageQueue:消息队列,用于存放Handler发送的消息,里面是一个链表结构,每个线程只有一个MessageQueue对象
- Looper:每个线程通过发送Message保存在MessageQueue中,Looper用于去消息队列中拿取消息;有两个重要的方法:prepare()方法,创建Looper对象并将其设置到ThreadLocal中; loop()方法,产生无限循环,每当发现一个消息,就从队列中拿取出来,最后传到Handler的handleMessage方法中。
- Handler:用于发送消息和处理消息;sendMessage方法、sendXXX等方法,最终是调用sendMessageAtTime方法;除了sendMessageAtFrontOfQueue方法;只要在Looper线程中构建Handler,那么这个Handler实例就会获取该Looper线程中的MessageQueue实例的引用,此后调用sendMessage方法就会通过此引用往消息队列插入消息。
附上自己画的一张图,有点丑,但可以将就将就
Handler的创建
1.在主线程中创建
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
2.在工作线程中创建
new Thread(new Runnable() {
@Override
public void run() {
//创建Looper
Looper.prepare();
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
//开始循环
Looper.loop();
}
});
问题:为什么子线程需要调用Looper.prepare()和Looper.loop()。这也是我刚开始不理解的地方。后来参考别的文章和阅读源码才知道。
Looper.prepare():
private static void prepare(boolean quitAllowed) {
//防止此方法执行两次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//将Looper对象设置到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
其中的ThreadLocal在上一篇有讲过用法;主要是在不同线程中设置不同的值。
Looper的构造方法
private Looper(boolean quitAllowed) {
//初始化消息队列和指定为当前线程
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
loop方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {//在这里进入循环,一直查看队列有没有消息
Message msg = queue.next(); // might block
if (msg == null) {
//队列没有消息则跳出
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//msg.target为Handler,此方法为将消息分发
msg.target.dispatchMessage(msg);
} finally {
……
msg.recycleUnchecked();
}
}
这个方法比较长,省略了其中一小部分的代码。
它的作用主要是进入死循环,从消息队列获取消息,如果消息队列没有消息则会停止循环。如果有找到就进行消息的分发
dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//这里就回调了handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//查看源码是一个空实现
}
}
进入此方法可知道如果创建Handler时有传进去Callback,则Callback回调了handleMessage方法。否则最后会回调Handler本身的handleMessage方法。
sendMessage方法:跟踪到最后是调用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);
}
enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//这里就是将消息加入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
消息队列中的enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
……
//同步,因为可能同时有多个消息加入(在不同线程中)
synchronized (this) {
……
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;
}
if (needWake) {//唤醒线程(Looper循环)
nativeWake(mPtr);//此方法是一个native的方法
}
}
return true;
}
从上面代码可知,往消息队列插入消息时,会分两种情况去插入消息,如果消息队列本身是空的,在最后则会唤醒主线程去处理消息
Handler的创建:
public Handler(Callback callback, boolean async) {
……
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这里顺便跟踪一下myLooper方法
public static @Nullable Looper myLooper() {
//从ThreadLocal中拿到Looper对象
return sThreadLocal.get();
}
总结
通过以上源码的分析,我们可以得出两点
- 可以知道为什么在创建Handler前要执行Looper.prepare方法。(因为没有先执行Looper.prepare()会抛异常)
- Handler一创建便持有本线程的消息队列和Looper对象,所以它可以在其他线程中发送消息,最后还是会回到创建时的线程(调用HandleMessage方法)。
疑惑
不知你也会跟我一样又产生一个疑惑,在主线程中创建Handler对象不需要调用Looper.prepare和Looper.loop。
查看了文章才大概明白了是为什么。在Activity的创建过程,走到ActivityThread中的main方法
public static void main(String[] args) {
……
Looper.prepareMainLooper();//!!!
ActivityThread thread = new ActivityThread();
thread.attach(false);
……
Looper.loop();//!!!
throw new RuntimeException("Main thread loop unexpectedly exited");
}
原来是在主线程中系统会执行:Looper.prepareMainLooper()和Looper.loop();
到此消息机制的源码分析也差不多啦,可能被我拆得比较碎,但希望我们都可以从中学习到东西。