Android应用在启动的时候会开启一条主线程,也叫UI线程,在UI线程里面不能执行一些耗时操作,不然的话会使UI失去响应,会出现ANR。所以我们执行一些耗时操作(比如下载)的话需要另开子线程来执行,执行完成后可能需要更新UI(比如将下载下来的信息显示出来),但是Android只允许在主线程里面更新UI,因为UI线程是线程不安全的,这应该就是Handler机制产生的原因。
Looper、Handler、MessageQueue、
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
Looper.prepare()方法会判断ThreadLocal是否已经保存了Looper,是则抛出异常,这说明,一个线程只能有一个Looper(),为什么说一个线程只有一个Looper(),这个只有弄清楚了ThreadLocal的工作原理才能明白。
简单说一下就是:
sThreadLocal.set(new Looper(true))//其实等同于以下
Thread.currentThread.ThreadLocalMap<ThreadLocal, Object>.put(sThreadLocal, Looper);
总结一下prepare()做的事情就是:判断当前线程是否已经有Looper了,有的话,抛出异常,没有的话,实例化一个Looper添加到ThreadLocal中,添加到ThreadLocal中,该Looper就和当前线程关联了。
对于ThreadLocal的理解可以参考http://blog.csdn.net/actor1999/article/details/52121303
获取Looper的方法,可以看到这个方法静态的。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper的构造方法,可以看到实例化了一个MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
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) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
可以看到,loop方法会执行一个死循环,从Message中取出数据 Message msg = queue.next(); 该方法可能会阻塞。当有Message的时候,调用msg.target.dispatchMessage(msg),这里的target是一个Handler对象,查看源码可以发现,该方法最终会调用handlerMessage()方法,而这个方法是个空方法,这也是我们处理消息的地方
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们在调用Handler.sendMessage(Message msg)其他方法也一样,最终都是调用以下这个方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
可以看到这个方法就是设置msg.target = this.
然后调用queue.enqueueMessage(msg, uptimeMillsi),这个方法其实就是将消息加入队列中。
Handler在实例化的时候,会保存当前线程的Looper,和该Looper的MeesageQueue,因此在实例化Handler的时候要先调用Looper.prepare();
为什么Looper要这么设计,一个线程只能有一个Looper,并且其构造函数是private的,我们从他出现的背景就能够领悟一二,文章开头说过了,他出现的背景是,子线程想更新UI,但是子线程又不能更新UI,只能通过这种消息机制来通知主线程更新UI,或者具体到,它就是在当前线程检索是否有消息需要传递的一个东西,一个线程只需要有一个检索的就够了。
总结下来,整个流程就是:
- 调用Looper.prepare(),创建和线程绑定的Looper,还有和Looper绑定的MessageQueue。
- 实例化Handler,获取Looper和MeesageQueue。重写其handMessage()方法。
- 调用Hander.sendMessage()方法,这个方法会将Message加入MessageQueue队列
- 然后loop()方法会将消息取出来,然后调用msg.target.dispatchMessage(msg)(Meesage.Handler.dispachMessage())最终调用我们重写的handlerMessage()方法。(loop()里面是一个死循环)