深入理解Handler,MessageQueue,Looper
转:http://www.jianshu.com/p/6d143b8c15ee
前言:
其实讲 Handler 内部机制的博客已经很多了,但是自己还是要在看一遍,源码是最好的资料。在具体看源码之前,有必要先理解一下 Handler、Looper、MessageQueue 以及 Message 他们的关系。
Looper: 是一个消息轮训器,他有一个叫 loop() 的方法,用于启动一个循环,不停的去轮询消息池
MessageQueue: 就是上面说到的消息池
Handler: 用于发送消息,和处理消息
Message: 一个消息对象
源码分析开始:
一切要从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;
}
我们可以看到,Handler定义了一个MessageQueue对象mQueue和一个Looper对象mLooper。顺着源码继续往下看,跳转到Looper.myLooper()方法。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里涉及了ThreadLocal,暂时不讲。通过ThreadLocal的get方法获取Looper并返回。那么问题来了?我们只看到了get,并没有看到set。如果没有set的话,get出来就会为null,通过Handler的构造函数我们知道,mLooper==null会抛出异常。而我们在使用Handler的过程中并没有遇到该异常。那问题来了,到底在哪里进行了set呢?通过对Looper源码搜索发现,改方法进行set操作:
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()方法中进行了set操作,那么问题又来了,哪里调用了该方法呢?因为prepare方法是私有方法,所以肯定是本类中调用,通过搜索发现以下方法调用了prepare()方法:
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
那一切就是顺利成章了。到这里,有人又会问,那这个方法又是谁调用的呢?看注释发现,该方法在启动app的时候就已经调用了。具体是在ActivityThread的main方法中启动。
到这里为止,我们了解了Handler,Looper的初始化相关知识。接下来,我们需要了解的是如何进行发送和处理Message。
发送Message代码如下:
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);
}
这里关键的是看懂msg.target = this;msg就是Message的对象,那么看Message源码发现,它的target属性是Handler target;那么,为什么发送消息的时候需要将this(当前Handler对象)带过去呢?咋们暂且继续…
这个方法实际执行的还是queue.enqueueMessage(),我们找到MessageQueue类的相关方法,发现以下代码
msg.next = p; // invariant: p == prev.next
prev.next = msg;
通过这两行代码我们发现,MessageQueue并不是队列,而是单链表。所以下次面试的时候,如果你支出handler的消息队列其实是利用Message的单链表实现的肯定能加分的。
到此,发送消息已经讲完了。下面我们看看处理消息是怎样进行的呢?我们知道,Loope会从MessageQueue中不断拿消息,我们看看Looper.loop()代码:
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
这里取出消息并分发之。是不是有顿悟的感觉,回到上面我们遗留的问题:msg.target = this,将this传递过去,言外之意就是哪个Handler发送的消息就由哪个Handler进行处理。那么我们来看看Handler的dispatchMessage 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
终于见到我们最常见的handleMessage()了。他首先判断 Message 对象的 callback 对象是不是为空,如果不为空,就直接调用 handleCallback 方法,并把 msg 对象传递过去,这样消息就被处理了,我们来看 Message 的 handleCallback 方法
private static void handleCallback(Message message) {
message.callback.run();
}
没什么好说的了,直接调用 Handler post 的 Runnable 对象的 run() 方法。
如果在发送消息时,我们没有给 Message 设置 callback 对象,那么程序会执行到 else 语句块,此时首先判断 Handler 的 mCallBack 对象是不是空的,如果不为空,直接调用 mCallback 的 handleMessage 方法进行消息处理。最终,只有当 Handler 的 mCallback 对象为空,才会执行自己的 handleMessage 方法。