之前看过若干次消息框架,隔了一段时间又记不起来一些细节了。这次补画了一些图,再理顺一下,有图的辅助,以后再看一下图就马上回忆起来了!
说起安卓的消息循环,你一定知道四个类:
Handler,Looper,ThreadLocal,MessageQueue。这次从源码的角度附加图做一个笔记。
基本的逻辑如下图,描述了主要类的结构以及他们是如何绑定到同一个线程的。请看下面的详细的描述。

Handler
Handler能够发送和处理和MessageQueue关联的 Message 或 Runnable。每个Handler和一个单独的线程关联,这个线程就是你创建这个Handler的时候所在的线程,需要处理的MessageQueue也是这个线程的MessageQueue。不信,你看:
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;
Handler有很多个构造函数,最终都会调用到这个构造函数。你可以看到Handler中的的Looper成员,就是通过Looper的静态方法myLooper得到的,myLooper是干啥的?你可以往下看Looper的内容,在这里你只要知道得到了一个和这个线程关联的Looper。如果 mLooper成员是null,那么就抛出异常。你可能会问,我在activity中随便创建handler啊,没有调用Looper.myLooper()方法。那是因为当你的应用运行的时候,Android已经通过Looper的静态方法prepareMainLooper创建了,这个方法只能执行一次,否则就会抛出异常了。这个Looper适合线程绑定的,你再看看mQueue,是从mLooper中拿到的。
Looper
Looper是做什么的?Looper的职能是为一个线程创建MessageQueue,绑定到这个线程,为此线程执行消息循环。
线程默认是没有looper的,除非你在线程调用prepare方法,然后才能执行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.");
/*省略若干代码*/
}
所以,在你写的线程中,可以这样使用:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
prepare做了什么呢?
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));}
你可以看到prepare()方法只能调用一次。在最后会创建一个Looper放在ThreadLocal里保存。关于ThreadLocal请看后文,这里你只要知道它是了一个和线程相关的存储结构。Looper是如何创建的呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,构造方法是私有的,新创建了一个MessageQueue,mThread就是当前线程。
那么Looper是如何执行消息循环的?
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;
for (;;) {
Message msg = queue.next();
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
可以看到,通过一个无限循环,不停的在消息队列中拿消息,将消息分发到指定的地方。
Message的target其实就是Handler
大多数和消息循环的交互都是通过Handler去完成的,就像你在Handler那部分看到的那样。记得在你不再执行消息循环的时候调用Looper的quit方法。
ThreadLocal
每个线程有一些和自己相关的变量,ThreadLocal的作用就是保存这些变量的。所有的变量是通过内部的静态类Value存储的。虽然,线程都是通过访问相同的ThreadLocal,但是每个线程保存的变量是分开的,不信,你看:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread); }
values.put(this, value);
}
上面的set方法中,values方法返回当前线程的localValues成员变量:
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
那么线程内部的localValues是什么?
public class Thread implements Runnable {
/**
* Normal thread local values.
*/
ThreadLocal.Values localValues;
/*省略若干代码*/
}
可以看到,Thread中的localValues是定义在ThreadLocal中线程本地的变量。如果在 values ()方法取得null值,就执行initializeValues方法。initializeValues是如何实现的呢?
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
然后将value的值放在当前线程的的localValues里。这样,虽然看起来访问的是用一个ThreadLocal,但是得到的值却是根据线程而不同的。
MessageQueue
MessageQueue是一个消息队列,包含成员变量Message mMessages;,可以理解成链表的头部。
内部包含5个native方法:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsIdling(long ptr);
这个类有很多native代码的调用,所以我并没有仔细看这个类,欢迎各位同学指点。